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