1 /// Provides a map (i.e. key/value) tuple. 2 module memgraph.map; 3 4 import std.string, std.conv; 5 6 import memgraph.mgclient, memgraph.detail, memgraph.value, memgraph.enums; 7 8 /// Sized sequence of pairs of keys and values. 9 /// Maximum possible map size allowed by Bolt protocol is `uint.max`. 10 /// 11 /// Map may contain a mixture of different types as values. A map owns all keys 12 /// and values stored in it. 13 /// 14 /// Can be used like a standard D hash map. 15 struct Map { 16 /// Create a shallow copy of `other` map. 17 @nogc this(inout ref Map other) { 18 this(other.ptr); 19 } 20 21 /// Create a map from a Value. 22 @nogc this(inout ref Value value) { 23 assert(value.type == Type.Map); 24 this(mg_value_map(value.ptr)); 25 } 26 27 /// Returns the value associated with the given `key`. 28 auto opIndex(const string key) const { 29 return Value(mg_map_at(ptr_, toStringz(key))); 30 } 31 32 /// Compares this map with `other`. 33 /// Return: true if same, false otherwise. 34 @nogc bool opEquals(const Map other) const { 35 return Detail.areMapsEqual(ptr_, other.ptr); 36 } 37 38 /// Return the hash code for this map. 39 size_t toHash() const nothrow @safe { 40 return cast(ulong)ptr_; 41 } 42 43 /// Checks if the map contains the given `key`. 44 /// Return: `true` if map contains `key`, `false` otherwise. 45 auto opBinaryRight(string op)(const char[] key) if (op == "in") { 46 return mg_map_at(ptr_, toStringz(key)) != null; 47 } 48 49 /// Returns the number of key / value pairs in this map. 50 @nogc @property uint length() const { return mg_map_size(ptr_); } 51 52 /// Return a printable string representation of this map. 53 string toString() const { 54 import std.array : appender; 55 auto str = appender!string; 56 str.put("{"); 57 immutable len = length; 58 for (uint i = 0; i < len; i++) { 59 str.put(Detail.convertString(mg_map_key_at(ptr_, i))); 60 str.put(":"); 61 str.put(to!string(Value(mg_map_value_at(ptr_, i)))); 62 if (i < len-1) 63 str.put(", "); 64 } 65 str.put("}"); 66 return str.data; 67 } 68 69 /// Checks if the map as range is empty. 70 @nogc bool empty() const { return idx_ >= length; } 71 72 /// Returns the next element in the map range. 73 @nogc auto front() const { 74 import std.typecons : Tuple; 75 assert(idx_ < length); 76 return Tuple!(string, "key", Value, "value")( 77 Detail.convertString(mg_map_key_at(ptr_, idx_)), 78 Value(mg_map_value_at(ptr_, idx_))); 79 } 80 81 /// Move to the next element in the list range. 82 @nogc void popFront() { idx_++; } 83 84 package: 85 /// Create a Map from a copy of the given `mg_map`. 86 @nogc this(inout mg_map *ptr) { 87 assert(ptr != null); 88 ptr_ = ptr; 89 } 90 91 /// Return pointer to internal `mg_map`. 92 @nogc auto ptr() inout { return ptr_; } 93 94 private: 95 const mg_map *ptr_; 96 uint idx_; 97 } // struct Map 98 99 unittest { 100 import std.range.primitives : isInputRange; 101 assert(isInputRange!Map); 102 } 103 104 unittest { 105 import testutils : connectContainer; 106 import std.algorithm : count; 107 import std.conv : to; 108 import memgraph.local_date_time; 109 110 auto client = connectContainer(); 111 assert(client); 112 113 auto result = client.execute(`return { 114 int_val: 123, 115 str_val: "Zdravo", 116 float_val: 3.1415, 117 bool_val: false, 118 dt_val: localdatetime('2021-12-13T12:34:56.100') 119 };`); 120 assert(result, client.error); 121 foreach (r; result) { 122 assert(r.length == 1); 123 assert(r[0].type() == Type.Map); 124 auto m = to!Map(r[0]); 125 assert(m.length == 5); 126 127 assert(m["int_val"].type() == Type.Int); 128 assert(m["str_val"].type() == Type.String); 129 assert(m["float_val"].type() == Type.Double); 130 assert(m["bool_val"].type() == Type.Bool); 131 assert(m["dt_val"].type() == Type.LocalDateTime); 132 133 assert(to!int(m["int_val"]) == 123); 134 assert(to!string(m["str_val"]) == "Zdravo"); 135 assert(to!double(m["float_val"]) == 3.1415); 136 assert(to!bool(m["bool_val"]) == false); 137 assert(to!LocalDateTime(m["dt_val"]).toString == "2021-Dec-13 12:34:56.1"); 138 139 assert(to!string(m) == "{bool_val:false, dt_val:2021-Dec-13 12:34:56.1, " ~ 140 "float_val:3.1415, int_val:123, str_val:Zdravo}", to!string(m)); 141 142 foreach (ref key, ref value; m) { 143 assert(key in m); 144 assert(m[key] == value); 145 } 146 147 const otherMap = m; 148 assert(otherMap == m); 149 150 assert(cast(ulong)m.ptr == m.toHash); 151 } 152 }