1 /// Provides a `Value` list. 2 module memgraph.list; 3 4 import std.string, std.conv; 5 6 import memgraph.mgclient, memgraph.detail, memgraph.value, memgraph.enums; 7 8 /// An ordered sequence of values. 9 /// 10 /// List may contain a mixture of different types as its elements. A list owns 11 /// all values stored in it. 12 /// 13 /// Maximum possible list length allowed by Bolt is `uint.max`. 14 struct List { 15 /// List needs an initial capacity. 16 @disable this(); 17 18 /// Construct a new list from an array of values. 19 this(const Value[] valueArray) { 20 this(mg_list_make_empty(to!uint(valueArray.length))); 21 foreach (ref value; valueArray) { 22 immutable rc = mg_list_append(ptr_, mg_value_copy(value.ptr)); 23 assert(rc == mg_error.MG_SUCCESS); 24 } 25 } 26 27 /// Constructs a list that can hold at most `capacity` elements. 28 /// Params: capacity = The maximum number of elements that the newly constructed 29 /// list can hold. 30 this(uint capacity) { 31 this(mg_list_make_empty(capacity)); 32 } 33 34 /// Create a copy of `other` list. 35 this(inout ref List other) { 36 this(mg_list_copy(other.ptr)); 37 } 38 39 /// Create a list from a Value. 40 this(inout ref Value value) { 41 assert(value.type == Type.List); 42 this(mg_list_copy(mg_value_list(value.ptr))); 43 } 44 45 /// Compares this list with `other`. 46 /// Return: true if same, false otherwise. 47 bool opEquals(const ref List other) const { 48 return Detail.areListsEqual(ptr_, other.ptr_); 49 } 50 51 /// Compares this list with an array of values. 52 /// Return: true if same, false otherwise. 53 bool opEquals(const ref Value[] valueArray) const { 54 auto other = List(valueArray); 55 return Detail.areListsEqual(ptr_, other.ptr_); 56 } 57 58 /// Append `value` to this list. 59 ref List opOpAssign(string op: "~")(const Value value) { 60 immutable rc = mg_list_append(ptr_, mg_value_copy(value.ptr)); 61 assert(rc == mg_error.MG_SUCCESS); 62 return this; 63 } 64 65 /// Return value at position `idx` of this list. 66 auto opIndex(size_t idx) const { 67 assert(ptr_ != null); 68 assert(idx < mg_list_size(ptr_)); 69 return Value(mg_list_at(ptr_, to!uint(idx))); 70 } 71 72 /// Return a printable string representation of this list. 73 const (string) toString() const { 74 assert(ptr_); 75 immutable len = length; 76 string ret = "["; 77 for (uint i = 0; i < len; i++) { 78 ret ~= to!string(Value(mg_list_at(ptr_, i))); 79 if (i < len-1) 80 ret ~= ","; 81 } 82 ret ~= "]"; 83 return ret; 84 } 85 86 /// Returns the number of values in this list. 87 @property uint length() const { 88 assert(ptr_ != null); 89 return mg_list_size(ptr_); 90 } 91 92 /// Checks if the list as range is empty. 93 @property bool empty() const { return idx_ >= length; } 94 95 /// Returns the next element in the list range. 96 auto front() const { 97 import std.typecons : Tuple; 98 assert(idx_ < length); 99 return Tuple!(uint, "index", Value, "value")(idx_, Value(mg_list_at(ptr_, idx_))); 100 } 101 102 /// Move to the next element in the list range. 103 void popFront() { idx_++; } 104 105 this(this) { 106 if (ptr_) 107 ptr_ = mg_list_copy(ptr_); 108 } 109 110 ~this() { 111 if (ptr_) 112 mg_list_destroy(ptr_); 113 } 114 115 package: 116 /// Create a List using the given `mg_list`. 117 this(mg_list *ptr) @trusted { 118 assert(ptr != null); 119 ptr_ = ptr; 120 } 121 122 /// Create a List from a copy of the given `mg_list`. 123 this(const mg_list *ptr) { 124 assert(ptr != null); 125 this(mg_list_copy(ptr)); 126 } 127 128 /// Return pointer to internal mg_list. 129 const (mg_list *) ptr() const { return ptr_; } 130 131 private: 132 mg_list *ptr_; 133 uint idx_; 134 } 135 136 unittest { 137 import std.range.primitives : isInputRange; 138 assert(isInputRange!List); 139 } 140 141 unittest { 142 auto l = List(42); 143 144 l ~= Value(42); 145 l ~= Value(23L); 146 l ~= Value(5.43210); 147 l ~= Value(true); 148 l ~= Value("Hi"); 149 assert(l.length == 5); 150 151 assert(l == l); 152 153 assert(l[0].type == Type.Int); 154 assert(l[0] == 42); 155 assert(l[1].type == Type.Int); 156 assert(l[1] == 23L); 157 assert(l[2].type == Type.Double); 158 assert(l[2] == 5.43210); 159 assert(l[3].type == Type.Bool); 160 assert(l[3] == true); 161 assert(l[4].type == Type.String); 162 assert(l[4] == "Hi"); 163 164 assert(l.ptr != null); 165 166 assert(to!string(l) == "[42,23,5.4321,true,Hi]"); 167 168 const List l2 = l; 169 170 assert(l2 == l); 171 172 assert(l2.ptr != null); 173 174 const List l3 = List(l2.ptr); 175 176 assert(l3 == l); 177 178 l ~= Value(123_456); 179 l ~= Value("Bok!"); 180 l ~= Value(true); 181 182 assert(l.length == 8); 183 assert(to!string(l) == "[42,23,5.4321,true,Hi,123456,Bok!,true]"); 184 185 auto v = Value(l); 186 assert(v == l); 187 assert(to!string(v) == to!string(l)); 188 assert(v == v); 189 190 const l4 = List(l); 191 assert(l4.ptr != null); 192 assert(l4.ptr != l.ptr); 193 assert(l4.length == 8); 194 assert(to!string(l4) == "[42,23,5.4321,true,Hi,123456,Bok!,true]"); 195 196 l ~= Value("another entry"); 197 assert(l.length == 9); 198 assert(to!string(l) == "[42,23,5.4321,true,Hi,123456,Bok!,true,another entry]"); 199 v = Value(l); 200 201 auto v2 = Value(l4); 202 assert(v2 == l4); 203 assert(to!string(v2) == to!string(l4)); 204 205 assert(v != v2); 206 } 207 208 unittest { 209 auto l1 = List(5); 210 l1 ~= Value(123); 211 l1 ~= Value("Hello"); 212 l1 ~= Value(true); 213 l1 ~= Value(5.5); 214 215 auto l2 = List(5); 216 l2 ~= Value(123); 217 l2 ~= Value("Hello"); 218 l2 ~= Value(true); 219 l2 ~= Value(5.5); 220 221 assert(l1 == l2); 222 223 l1 ~= Value("new"); 224 l2 ~= Value("novo"); 225 226 assert(l1 != l2); 227 } 228 229 unittest { 230 Value[] vl; 231 vl ~= Value(42); 232 vl ~= Value(23L); 233 vl ~= Value(5.43210); 234 vl ~= Value(true); 235 vl ~= Value("Hi"); 236 assert(vl.length == 5); 237 238 auto l = List(vl); 239 foreach (i, ref v; vl) 240 assert(v == l[i]); 241 foreach (i, ref v; l) 242 assert(v == vl[i]); 243 244 assert(l == vl); 245 }