1 /// Provides a wrapper for a Bolt value.
2 module memgraph.value;
3 
4 import std.string : toStringz;
5 import std.typecons : tuple;
6 import std.conv : to;
7 
8 import memgraph.detail;
9 import memgraph;
10 
11 // Internal mapping between D type and a tuple containing the memgraph type, the
12 // operation to be applied when doing an opCast/opEquals, and the operation to be
13 // applied during construction of a value or an opAssign.
14 private static immutable enum mixinOps = [
15 	typeid(double): tuple(Type.Double,
16 		"mg_value_float(ptr_)", "mg_value_make_float(val)"),
17 	typeid(int): tuple(Type.Int,
18 		"to!int(mg_value_integer(ptr_))", "mg_value_make_integer(val)"),
19 	typeid(long): tuple(Type.Int,
20 		"mg_value_integer(ptr_)", "mg_value_make_integer(val)"),
21 	typeid(bool): tuple(Type.Bool,
22 		"to!bool(mg_value_bool(ptr_))", "mg_value_make_bool(val)"),
23 	typeid(Node): tuple(Type.Node,
24 		"Node(mg_value_node(ptr_))", "mg_value_make_node(mg_node_copy(val.ptr))"),
25 	typeid(List): tuple(Type.List,
26 		"List(mg_value_list(ptr_))", "mg_value_make_list(mg_list_copy(val.ptr))"),
27 	typeid(Map): tuple(Type.Map,
28 		"Map(mg_value_map(ptr_))", "mg_value_make_map(mg_map_copy(val.ptr))"),
29 	typeid(Path): tuple(Type.Path,
30 		"Path(mg_value_path(ptr_))", "mg_value_make_path(mg_path_copy(val.ptr))"),
31 	typeid(Relationship): tuple(Type.Relationship,
32 		"Relationship(mg_value_relationship(ptr_))", "mg_value_make_relationship(mg_relationship_copy(val.ptr))"),
33 	typeid(UnboundRelationship): tuple(Type.UnboundRelationship,
34 		"UnboundRelationship(mg_value_unbound_relationship(ptr_))", "mg_value_make_unbound_relationship(mg_unbound_relationship_copy(val.ptr))"),
35 	typeid(string): tuple(Type.String,
36 		"Detail.convertString(mg_value_string(ptr_))", "mg_value_make_string(toStringz(val))"),
37 	typeid(char[]): tuple(Type.String,
38 		"Detail.convertString(mg_value_string(ptr_))", "mg_value_make_string(toStringz(val))"),
39 	typeid(Date): tuple(Type.Date,
40 		"Date(mg_value_date(ptr_))", "mg_value_make_date(mg_date_copy(val.ptr))"),
41 	typeid(Time): tuple(Type.Time,
42 		"Time(mg_value_time(ptr_))", "mg_value_make_time(mg_time_copy(val.ptr))"),
43 	typeid(LocalTime): tuple(Type.LocalTime,
44 		"LocalTime(mg_value_local_time(ptr_))", "mg_value_make_local_time(mg_local_time_copy(val.ptr))"),
45 	typeid(DateTime): tuple(Type.DateTime,
46 		"DateTime(mg_value_date_time(ptr_))", "mg_value_make_date_time(mg_date_time_copy(val.ptr))"),
47 	typeid(DateTimeZoneId): tuple(Type.DateTimeZoneId,
48 		"DateTimeZoneId(mg_value_date_time_zone_id(ptr_))", "mg_value_make_date_time_zone_id(mg_date_time_zone_id_copy(val.ptr))"),
49 	typeid(LocalDateTime): tuple(Type.LocalDateTime,
50 		"LocalDateTime(mg_value_local_date_time(ptr_))", "mg_value_make_local_date_time(mg_local_date_time_copy(val.ptr))"),
51 	typeid(Duration): tuple(Type.Duration,
52 		"Duration(mg_value_duration(ptr_))", "mg_value_make_duration(mg_duration_copy(val.ptr))"),
53 	typeid(Point2d): tuple(Type.Point2d,
54 		"Point2d(mg_value_point_2d(ptr_))", "mg_value_make_point_2d(mg_point_2d_copy(val.ptr))"),
55 	typeid(Point3d): tuple(Type.Point3d,
56 		"Point3d(mg_value_point_3d(ptr_))", "mg_value_make_point_3d(mg_point_3d_copy(val.ptr))"),
57 ];
58 
59 /// A Bolt value, encapsulating all other values.
60 struct Value {
61 	/// Make a Null value.
62 	this(typeof(null)) { this(mg_value_make_null()); }
63 
64 	/// Copy constructor.
65 	this(const ref Value rhs) {
66 		this(rhs.ptr_);
67 	}
68 
69 	/// Create a copy of the internal `mg_value`.
70 	this(this) {
71 		if (ptr_)
72 			ptr_ = mg_value_copy(ptr_);
73 	}
74 
75 	/// Make a new value of type `T` and initialise it with `val`.
76 	this(T)(const T val) {
77 		this(mixin(mixinOps[typeid(T)][2]));
78 	}
79 
80 	/// Cast this value to type `T`.
81 	/// Note: The code asserts that the current value holds a representation of type `T`.
82 	auto opCast(T)() const {
83 		assert(ptr_ != null);
84 		assert(type == mixinOps[typeid(T)][0]);
85 		return mixin(mixinOps[typeid(T)][1]);
86 	}
87 
88 	/// Comparison operator for type `T`.
89 	/// Note: The code asserts that the current value holds a representation of type `T`.
90 	bool opEquals(T)(const T val) const {
91 		assert(ptr_ != null);
92 		assert(type == mixinOps[typeid(T)][0]);
93 		return mixin(mixinOps[typeid(T)][1]) == val;
94 	}
95 
96 	/// Assignment operator for type `T`.
97 	ref Value opAssign(T)(const T val) return {
98 		if (ptr_)
99 			mg_value_destroy(ptr_);
100 		ptr_ = mixin(mixinOps[typeid(T)][2]);
101 		return this;
102 	}
103 
104 	/// Comparison operator for another `Value`.
105 	bool opEquals(const Value other) const {
106 		return Detail.areValuesEqual(ptr_, other.ptr_);
107 	}
108 
109 	/// Assignment operator for another `Value`.
110 	ref Value opAssign(Value value) @safe return {
111 		import std.algorithm.mutation : swap;
112 		swap(this, value);
113 		return this;
114 	}
115 
116 	/// Return this value as a string.
117 	/// If the value held is not of type `Type.String`, then
118 	/// it will be first converted into the appropriate string
119 	/// representation.
120 	string toString() const {
121 		final switch (type) {
122 			case Type.Null:					return "(null)";
123 			case Type.Double:				return to!string(to!double(this));
124 			case Type.Node:					return to!string(to!Node(this));
125 			case Type.Bool:					return to!string(to!bool(this));
126 			case Type.Int:					return to!string(to!int(this));
127 			case Type.String:				return Detail.convertString(mg_value_string(ptr_));
128 			case Type.Relationship:			return to!string(to!Relationship(this));
129 			case Type.UnboundRelationship:	return to!string(to!UnboundRelationship(this));
130 			case Type.List:					return to!string(to!List(this));
131 			case Type.Map:					return to!string(to!Map(this));
132 			case Type.Path:					return to!string(to!Path(this));
133 			case Type.Date:					return to!string(to!Date(this));
134 			case Type.Time:					return to!string(to!Time(this));
135 			case Type.LocalTime:			return to!string(to!LocalTime(this));
136 			case Type.DateTime:				return to!string(to!DateTime(this));
137 			case Type.DateTimeZoneId:		return to!string(to!DateTimeZoneId(this));
138 			case Type.LocalDateTime:		return to!string(to!LocalDateTime(this));
139 			case Type.Duration:				return to!string(to!Duration(this));
140 			case Type.Point2d:				return to!string(to!Point2d(this));
141 			case Type.Point3d:				return to!string(to!Point3d(this));
142 		}
143 	}
144 
145 	/// Return the type of value being held.
146 	@property Type type() const {
147 		assert(ptr_ != null);
148 		return Detail.convertType(mg_value_get_type(ptr_));
149 	}
150 
151 	/// Destroys the internal `mg_value`.
152 	@safe @nogc ~this() {
153 		if (ptr_)
154 			mg_value_destroy(ptr_);
155 	}
156 
157 package:
158 	/// Create a Value using the given `mg_value`.
159 	this(mg_value *p) {
160 		assert(p != null);
161 		ptr_ = p;
162 	}
163 
164 	/// Create a Value from a copy of the given `mg_value`.
165 	this(const mg_value *p) {
166 		assert(p != null);
167 		this(mg_value_copy(p));
168 	}
169 
170 	/// Returns internal `mg_value` pointer.
171 	const (mg_value *) ptr() const { return ptr_; }
172 
173 private:
174 	mg_value *ptr_;
175 }
176 
177 // string tests
178 unittest {
179 	auto v1 = Value("Zdravo, svijete!");
180 	assert(v1.type == Type.String);
181 	assert(v1 == "Zdravo, svijete!");
182 
183 	auto v2 = v1;
184 	assert(v1.type == v2.type);
185 	assert(v1 == v2);
186 	assert(v2 == "Zdravo, svijete!");
187 
188 	assert(v1.toString == "Zdravo, svijete!");
189 	assert(to!string(v1) == "Zdravo, svijete!");
190 
191 	v2 = "Hello there";
192 	assert(v2 == "Hello there");
193 	assert(v2.toString == "Hello there");
194 	assert(to!string(v2) == "Hello there");
195 
196 	assert(v1 == v1);
197 }
198 
199 unittest {
200 	const v1 = Value("Zdravo, svijete!");
201 	assert(v1.type == Type.String);
202 	assert(v1 == "Zdravo, svijete!");
203 
204 	const v2 = v1;
205 	assert(v1.type == v2.type);
206 	assert(v1 == v2);
207 	assert(v2 == "Zdravo, svijete!");
208 
209 	assert(v1.toString == "Zdravo, svijete!");
210 	assert(to!string(v1) == "Zdravo, svijete!");
211 }
212 
213 // long/int tests
214 unittest {
215 	auto v1 = Value(42L);
216 	assert(v1.type == Type.Int);
217 	assert(v1 == 42);
218 	assert(v1 == 42L);
219 
220 	const v2 = v1;
221 	assert(v1.type == v2.type);
222 	assert(v1 == v2);
223 	assert(v2 == 42);
224 
225 	const v3 = Value(42);
226 	assert(v1.type == v3.type);
227 	assert(v1 == v3);
228 	assert(v3 == 42);
229 
230 	assert(v1.toString == "42");
231 
232 	v1 = 23;
233 	assert(v1 == 23);
234 	assert(to!int(v1) == 23);
235 	assert(to!long(v1) == 23);
236 }
237 
238 unittest {
239 	const v1 = Value(42L);
240 	assert(v1.type == Type.Int);
241 	assert(v1 == 42);
242 	assert(v1 == 42L);
243 
244 	const v2 = v1;
245 	assert(v1.type == v2.type);
246 	assert(v1 == v2);
247 	assert(v2 == 42);
248 
249 	const v3 = Value(42);
250 	assert(v1.type == v3.type);
251 	assert(v1 == v3);
252 	assert(v3 == 42);
253 
254 	assert(v1.toString == "42");
255 }
256 
257 // bool tests
258 unittest {
259 	auto v1 = Value(true);
260 	assert(v1.type == Type.Bool);
261 	assert(v1 == true);
262 
263 	const v2 = v1;
264 	assert(v1.type == v2.type);
265 	assert(v1 == v2);
266 	assert(v2 == true);
267 
268 	assert(v1.toString == "true");
269 
270 	assert(to!bool(v1) == true);
271 	assert(v1 == true);
272 }
273 
274 unittest {
275 	const v1 = Value(true);
276 	assert(v1.type == Type.Bool);
277 	assert(v1 == true);
278 
279 	const v2 = v1;
280 	assert(v1.type == v2.type);
281 	assert(v1 == v2);
282 	assert(v2 == true);
283 
284 	assert(v1.toString == "true");
285 
286 	assert(to!bool(v1) == true);
287 	assert(v1 == true);
288 }
289 
290 // double tests
291 unittest {
292 	auto v1 = Value(3.1415926);
293 	assert(v1.type == Type.Double);
294 	assert(v1 == 3.1415926);
295 
296 	const v2 = v1;
297 	assert(v1.type == v2.type);
298 	assert(v1 == v2);
299 	assert(v2 == 3.1415926);
300 
301 	assert(v1.toString == "3.14159");
302 
303 	assert(to!double(v1) == 3.1415926);
304 	assert(v1 == 3.1415926);
305 }
306 
307 unittest {
308 	const v1 = Value(3.1415926);
309 	assert(v1.type == Type.Double);
310 	assert(v1 == 3.1415926);
311 
312 	const v2 = v1;
313 	assert(v1.type == v2.type);
314 	assert(v1 == v2);
315 	assert(v2 == 3.1415926);
316 
317 	assert(v1.toString == "3.14159");
318 
319 	assert(to!double(v1) == 3.1415926);
320 	assert(v1 == 3.1415926);
321 }
322 
323 // list tests
324 unittest {
325 	auto l = List(8);
326 	l ~= Value(123);
327 	l ~= Value("Hello");
328 	l ~= Value(3.21);
329 	l ~= Value(true);
330 
331 	assert(l.length == 4);
332 
333 	auto v = Value(l);
334 	assert(v.type == Type.List);
335 
336 	assert(l == v);
337 	assert(v == l);
338 
339 	const v2 = Value(l);
340 	assert(v == v2);
341 
342 	const l2 = to!List(v);
343 	assert(l2 == l);
344 }
345 
346 // map tests
347 unittest {
348 	auto m = Map(100);
349 	m["key1"] = 1;
350 	m["key2"] = true;
351 	m["key3"] = 2.71828;
352 	m["key4"] = "test";
353 
354 	auto v1 = Value(m);
355 	assert(v1.type == Type.Map);
356 
357 	const v2 = Value(m);
358 	assert(v2.type == Type.Map);
359 
360 	assert(v1 == v2);
361 
362 	const m2 = to!Map(v1);
363 	assert(m == m2);
364 }
365 
366 // null tests
367 unittest {
368 	const v1 = Value(null);
369 	assert(v1.type == Type.Null);
370 	const v2 = Value(null);
371 	assert(v2.type == Type.Null);
372 
373 	assert(v1 == v2);
374 
375 	assert(to!string(v1) == "(null)");
376 }
377 
378 // unknown value test
379 unittest {
380 	import std.exception, core.exception;
381 
382 	auto v = Value(1);
383 	assert(v.type == Type.Int);
384 
385 	v.ptr_.type = mg_value_type.MG_VALUE_TYPE_UNKNOWN;
386 
387 	assertThrown!AssertError(v.type);
388 
389 	auto v2 = Value(1);
390 	v2.ptr_.type = mg_value_type.MG_VALUE_TYPE_UNKNOWN;
391 
392 	assertThrown!AssertError(v == v2);
393 
394 	v.ptr_.type = cast(mg_value_type)-1;
395 
396 	assertThrown!AssertError(v.type);
397 
398 	v2.ptr_.type = cast(mg_value_type)-1;
399 
400 	assertThrown!AssertError(v == v2);
401 }
402 
403 // comparison tests
404 unittest {
405 	const v1 = Value(1);
406 	assert(v1.type == Type.Int);
407 	assert(v1 == v1);
408 
409 	const v2 = Value(2.71828);
410 	assert(v2.type == Type.Double);
411 	assert(v1 != v2);
412 }
413 
414 // assignment tests
415 unittest {
416 	Value v;
417 	v = 42;
418 	assert(v == 42);
419 	v = 2.71828;
420 	assert(v == 2.71828);
421 	v = "Hello";
422 	assert(v == "Hello");
423 	v = true;
424 	assert(v == true);
425 
426 	auto l = List(10);
427 	v = l;
428 
429 	auto v2 = Value(v);
430 	assert(v == v2);
431 }