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 }