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 /// Create a shallow copy of `other` list. 16 @nogc this(inout ref List other) { 17 this(other.ptr_); 18 } 19 20 /// Create a shallow list copy from a Value. 21 @nogc this(inout ref Value value) { 22 assert(value.type == Type.List); 23 this(mg_value_list(value.ptr)); 24 } 25 26 /// Compares this list with `other`. 27 /// Return: true if same, false otherwise. 28 @nogc auto opEquals(const ref List other) const { 29 return Detail.areListsEqual(ptr_, other.ptr_); 30 } 31 32 /// Return the hash code for this list. 33 size_t toHash() const nothrow @safe { 34 return cast(ulong)ptr_; 35 } 36 37 /// Return value at position `idx` of this list. 38 @nogc auto opIndex(uint idx) const { 39 assert(idx < length); 40 return Value(mg_list_at(ptr_, idx)); 41 } 42 43 /// Returns the number of values in this list. 44 @nogc @property uint length() const { return mg_list_size(ptr_); } 45 46 /// Return a printable string representation of this list. 47 string toString() const { 48 import std.array : appender; 49 auto str = appender!string; 50 str.put("["); 51 immutable len = length; 52 for (uint i = 0; i < len; i++) { 53 str.put(to!string(Value(mg_list_at(ptr_, i)))); 54 if (i < len-1) 55 str.put(", "); 56 } 57 str.put("]"); 58 return str.data; 59 } 60 61 /// Checks if the list as range is empty. 62 @nogc @property bool empty() const { return idx_ >= length; } 63 64 /// Returns the next element in the list range. 65 @nogc @property auto front() const { 66 import std.typecons : Tuple; 67 assert(idx_ < length); 68 return Tuple!(uint, "index", Value, "value")(idx_, Value(mg_list_at(ptr_, idx_))); 69 } 70 71 /// Move to the next element in the list range. 72 @nogc void popFront() { idx_++; } 73 74 package: 75 /// Create a List using the given `mg_list` pointer. 76 @nogc this(const mg_list *ptr) { 77 assert(ptr != null); 78 ptr_ = ptr; 79 } 80 81 /// Return pointer to internal `mg_list`. 82 @nogc auto ptr() inout { return ptr_; } 83 84 private: 85 const mg_list *ptr_; 86 uint idx_; 87 } // struct List 88 89 unittest { 90 import std.range.primitives : isInputRange; 91 assert(isInputRange!List); 92 } 93 94 unittest { 95 import testutils : connectContainer; 96 import std.algorithm : count; 97 import std.conv : to; 98 import memgraph.local_date_time; 99 100 auto client = connectContainer(); 101 assert(client); 102 103 auto result = client.execute(`return [1, 2, 3, 4.56, true, "Hello", localdatetime('2021-12-13T12:34:56.100')];`); 104 assert(result, client.error); 105 foreach (r; result) { 106 assert(r.length == 1); 107 assert(r[0].type() == Type.List); 108 auto list = to!List(r[0]); 109 assert(list.length == 7); 110 111 assert(list[0].type() == Type.Int); 112 assert(list[1].type() == Type.Int); 113 assert(list[2].type() == Type.Int); 114 assert(list[3].type() == Type.Double); 115 assert(list[4].type() == Type.Bool); 116 assert(list[5].type() == Type.String); 117 assert(list[6].type() == Type.LocalDateTime); 118 119 assert(to!int(list[0]) == 1); 120 assert(to!int(list[1]) == 2); 121 assert(to!int(list[2]) == 3); 122 assert(to!double(list[3]) == 4.56); 123 assert(to!bool(list[4]) == true); 124 assert(to!string(list[5]) == "Hello"); 125 assert(to!LocalDateTime(list[6]).toString == "2021-Dec-13 12:34:56.1"); 126 127 assert(to!string(list) == "[1, 2, 3, 4.56, true, Hello, 2021-Dec-13 12:34:56.1]", to!string(list)); 128 129 const otherList = list; 130 assert(otherList == list); 131 132 foreach (ref idx, ref value; list) { 133 assert(value == list[idx]); 134 } 135 136 assert(list.ptr != null); 137 assert(list.ptr == otherList.ptr); 138 139 assert(cast(ulong)list.ptr == list.toHash); 140 } 141 }