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 }