1 /// Provides helper functions.
2 module memgraph.detail;
3 
4 import std.conv;
5 
6 import memgraph.mgclient, memgraph.enums;
7 
8 /// Wrapper class around static helper functions.
9 struct Detail {
10 	/// Converts a `mg_string` to a D string.
11 	static string convertString(const mg_string *str) {
12 		assert(str != null);
13 		const auto data = mg_string_data(str);
14 		const auto len = mg_string_size(str);
15 		return to!string(data[0..len]);
16 	}
17 
18 	/// Converts a `mg_value_type` enum to a `Type` enum.
19 	static Type convertType(mg_value_type type) {
20 		switch (type) {
21 			case mg_value_type.MG_VALUE_TYPE_NULL:
22 				return Type.Null;
23 			case mg_value_type.MG_VALUE_TYPE_BOOL:
24 				return Type.Bool;
25 			case mg_value_type.MG_VALUE_TYPE_INTEGER:
26 				return Type.Int;
27 			case mg_value_type.MG_VALUE_TYPE_FLOAT:
28 				return Type.Double;
29 			case mg_value_type.MG_VALUE_TYPE_STRING:
30 				return Type.String;
31 			case mg_value_type.MG_VALUE_TYPE_LIST:
32 				return Type.List;
33 			case mg_value_type.MG_VALUE_TYPE_MAP:
34 				return Type.Map;
35 			case mg_value_type.MG_VALUE_TYPE_NODE:
36 				return Type.Node;
37 			case mg_value_type.MG_VALUE_TYPE_RELATIONSHIP:
38 				return Type.Relationship;
39 			case mg_value_type.MG_VALUE_TYPE_UNBOUND_RELATIONSHIP:
40 				return Type.UnboundRelationship;
41 			case mg_value_type.MG_VALUE_TYPE_PATH:
42 				return Type.Path;
43 			case mg_value_type.MG_VALUE_TYPE_DATE:
44 				return Type.Date;
45 			case mg_value_type.MG_VALUE_TYPE_TIME:
46 				return Type.Time;
47 			case mg_value_type.MG_VALUE_TYPE_LOCAL_TIME:
48 				return Type.LocalTime;
49 			case mg_value_type.MG_VALUE_TYPE_DATE_TIME:
50 				return Type.DateTime;
51 			case mg_value_type.MG_VALUE_TYPE_DATE_TIME_ZONE_ID:
52 				return Type.DateTimeZoneId;
53 			case mg_value_type.MG_VALUE_TYPE_LOCAL_DATE_TIME:
54 				return Type.LocalDateTime;
55 			case mg_value_type.MG_VALUE_TYPE_DURATION:
56 				return Type.Duration;
57 			case mg_value_type.MG_VALUE_TYPE_POINT_2D:
58 				return Type.Point2d;
59 			case mg_value_type.MG_VALUE_TYPE_POINT_3D:
60 				return Type.Point3d;
61 			case mg_value_type.MG_VALUE_TYPE_UNKNOWN:
62 				assert(0, "unknown value type");
63 			default:
64 				assert(0, "unexpected value type: " ~ to!string(type));
65 		}
66 	}
67 
68 	/// Compares two `mg_value`s.
69 	static bool areValuesEqual(const mg_value *value1, const mg_value *value2) {
70 		assert(value1 != null);
71 		assert(value2 != null);
72 		if (value1 == value2) {
73 			return true;
74 		}
75 		if (mg_value_get_type(value1) != mg_value_get_type(value2)) {
76 			return false;
77 		}
78 		switch (mg_value_get_type(value1)) {
79 			case mg_value_type.MG_VALUE_TYPE_NULL:
80 				return true;
81 			case mg_value_type.MG_VALUE_TYPE_BOOL:
82 				return mg_value_bool(value1) == mg_value_bool(value2);
83 			case mg_value_type.MG_VALUE_TYPE_INTEGER:
84 				return mg_value_integer(value1) == mg_value_integer(value2);
85 			case mg_value_type.MG_VALUE_TYPE_FLOAT:
86 				return mg_value_float(value1) == mg_value_float(value2);
87 			case mg_value_type.MG_VALUE_TYPE_STRING:
88 				return Detail.convertString(mg_value_string(value1)) ==
89 					Detail.convertString(mg_value_string(value2));
90 			case mg_value_type.MG_VALUE_TYPE_LIST:
91 				return Detail.areListsEqual(mg_value_list(value1),
92 						mg_value_list(value2));
93 			case mg_value_type.MG_VALUE_TYPE_MAP:
94 				return Detail.areMapsEqual(mg_value_map(value1), mg_value_map(value2));
95 			case mg_value_type.MG_VALUE_TYPE_NODE:
96 				return Detail.areNodesEqual(mg_value_node(value1),
97 						mg_value_node(value2));
98 			case mg_value_type.MG_VALUE_TYPE_RELATIONSHIP:
99 				return Detail.areRelationshipsEqual(mg_value_relationship(value1),
100 						mg_value_relationship(value2));
101 			case mg_value_type.MG_VALUE_TYPE_UNBOUND_RELATIONSHIP:
102 				return Detail.areUnboundRelationshipsEqual(
103 						mg_value_unbound_relationship(value1),
104 						mg_value_unbound_relationship(value2));
105 			case mg_value_type.MG_VALUE_TYPE_PATH:
106 				return Detail.arePathsEqual(mg_value_path(value1),
107 						mg_value_path(value2));
108 			case mg_value_type.MG_VALUE_TYPE_DATE:
109 				return Detail.areDatesEqual(mg_value_date(value1),
110 						mg_value_date(value2));
111 			case mg_value_type.MG_VALUE_TYPE_TIME:
112 				return Detail.areTimesEqual(mg_value_time(value1),
113 						mg_value_time(value2));
114 			case mg_value_type.MG_VALUE_TYPE_LOCAL_TIME:
115 				return Detail.areLocalTimesEqual(mg_value_local_time(value1),
116 						mg_value_local_time(value2));
117 			case mg_value_type.MG_VALUE_TYPE_DATE_TIME:
118 				return Detail.areDateTimesEqual(mg_value_date_time(value1),
119 						mg_value_date_time(value2));
120 			case mg_value_type.MG_VALUE_TYPE_DATE_TIME_ZONE_ID:
121 				return Detail.areDateTimeZoneIdsEqual(
122 						mg_value_date_time_zone_id(value1),
123 						mg_value_date_time_zone_id(value2));
124 			case mg_value_type.MG_VALUE_TYPE_LOCAL_DATE_TIME:
125 				return Detail.areLocalDateTimesEqual(mg_value_local_date_time(value1),
126 						mg_value_local_date_time(value2));
127 			case mg_value_type.MG_VALUE_TYPE_DURATION:
128 				return Detail.areDurationsEqual(mg_value_duration(value1),
129 						mg_value_duration(value2));
130 			case mg_value_type.MG_VALUE_TYPE_POINT_2D:
131 				return Detail.arePoint2dsEqual(mg_value_point_2d(value1),
132 						mg_value_point_2d(value2));
133 			case mg_value_type.MG_VALUE_TYPE_POINT_3D:
134 				return Detail.arePoint3dsEqual(mg_value_point_3d(value1),
135 						mg_value_point_3d(value2));
136 			case mg_value_type.MG_VALUE_TYPE_UNKNOWN:
137 				assert(0, "comparing values of unknown types!");
138 			default: assert(0, "unexpected type: " ~ to!string(mg_value_get_type(value1)));
139 		}
140 	}
141 
142 	/// Compares two `mg_list`s.
143 	static bool areListsEqual(const mg_list *list1, const mg_list *list2) {
144 		assert(list1 != null);
145 		assert(list2 != null);
146 		if (list1 == list2)
147 			return true;
148 		if (mg_list_size(list1) != mg_list_size(list2))
149 			return false;
150 		const uint len = mg_list_size(list1);
151 		for (uint i = 0; i < len; ++i) {
152 			if (!Detail.areValuesEqual(mg_list_at(list1, i), mg_list_at(list2, i)))
153 				return false;
154 		}
155 		return true;
156 	}
157 
158 	/// Compares two `mg_map`s.
159 	static bool areMapsEqual(const mg_map *map1, const mg_map *map2) {
160 		assert(map1 != null);
161 		assert(map2 != null);
162 		if (map1 == map2)
163 			return true;
164 		if (mg_map_size(map1) != mg_map_size(map2))
165 			return false;
166 		const uint len = mg_map_size(map1);
167 		for (uint i = 0; i < len; ++i) {
168 			const mg_string *key = mg_map_key_at(map1, i);
169 			const mg_value *value1 = mg_map_value_at(map1, i);
170 			const mg_value *value2 =
171 				mg_map_at2(map2, mg_string_size(key), mg_string_data(key));
172 			if (value2 == null)
173 				return false;
174 			if (!Detail.areValuesEqual(value1, value2))
175 				return false;
176 		}
177 		return true;
178 	}
179 
180 	/// Compares two nodes for equality.
181 	/// Params: node1 = first node to compare
182 	///         node2 = second node to compare
183 	/// Return: `true` if both nodes are equal, `false` otherwise
184 	static bool areNodesEqual(const mg_node *node1, const mg_node *node2) {
185 		assert(node1 != null);
186 		assert(node2 != null);
187 		if (node1 == node2)
188 			return true;
189 		if (mg_node_id(node1) != mg_node_id(node2))
190 			return false;
191 		if (mg_node_label_count(node1) != mg_node_label_count(node2))
192 			return false;
193 		string[] labels1;
194 		string[] labels2;
195 		const uint label_count = mg_node_label_count(node1);
196 		labels1.length = labels2.length = label_count;
197 		for (uint i = 0; i < label_count; ++i) {
198 			labels1[i] = Detail.convertString(mg_node_label_at(node1, i));
199 			labels2[i] = Detail.convertString(mg_node_label_at(node2, i));
200 		}
201 		if (labels1 != labels2)
202 			return false;
203 		return Detail.areMapsEqual(mg_node_properties(node1),
204 				mg_node_properties(node2));
205 	}	// areNodesEqual()
206 
207 	/// Compares two `mg_relationship`s.
208 	static bool areRelationshipsEqual(const mg_relationship *rel1,
209 			const mg_relationship *rel2) {
210 		assert(rel1 != null);
211 		assert(rel2 != null);
212 		if (rel1 == rel2)
213 			return true;
214 		if (mg_relationship_id(rel1) != mg_relationship_id(rel2))
215 			return false;
216 		if (mg_relationship_start_id(rel1) != mg_relationship_start_id(rel2))
217 			return false;
218 		if (mg_relationship_end_id(rel1) != mg_relationship_end_id(rel2))
219 			return false;
220 		if (Detail.convertString(mg_relationship_type(rel1)) !=
221 				Detail.convertString(mg_relationship_type(rel2)))
222 			return false;
223 		return Detail.areMapsEqual(mg_relationship_properties(rel1),
224 				mg_relationship_properties(rel2));
225 	}
226 
227 	/// Compares two `mg_unbound_relationship`s.
228 	static bool areUnboundRelationshipsEqual(const mg_unbound_relationship *rel1,
229 			const mg_unbound_relationship *rel2) {
230 		assert(rel1 != null);
231 		assert(rel2 != null);
232 		if (rel1 == rel2)
233 			return true;
234 		if (mg_unbound_relationship_id(rel1) != mg_unbound_relationship_id(rel2))
235 			return false;
236 		if (Detail.convertString(mg_unbound_relationship_type(rel1)) !=
237 				Detail.convertString(mg_unbound_relationship_type(rel2)))
238 			return false;
239 		return Detail.areMapsEqual(mg_unbound_relationship_properties(rel1),
240 				mg_unbound_relationship_properties(rel2));
241 	}
242 
243 	/// Compares two `mg_path`s.
244 	static bool arePathsEqual(const mg_path *path1, const mg_path *path2) {
245 		assert(path1 != null);
246 		assert(path2 != null);
247 		if (path1 == path2)
248 			return true;
249 		if (mg_path_length(path1) != mg_path_length(path2))
250 			return false;
251 		const uint len = mg_path_length(path1);
252 		for (uint i = 0; i < len; ++i) {
253 			if (!Detail.areNodesEqual(mg_path_node_at(path1, i),
254 						mg_path_node_at(path2, i))) {
255 				return false;
256 			}
257 			if (!Detail.areUnboundRelationshipsEqual(
258 						mg_path_relationship_at(path1, i),
259 						mg_path_relationship_at(path2, i))) {
260 				return false;
261 			}
262 			if (mg_path_relationship_reversed_at(path1, i) !=
263 					mg_path_relationship_reversed_at(path2, i)) {
264 				return false;
265 			}
266 		}
267 		return Detail.areNodesEqual(mg_path_node_at(path1, len),
268 				mg_path_node_at(path2, len));
269 	}
270 
271 	/// Compares two `mg_date`s.
272 	static bool areDatesEqual(const mg_date *date1, const mg_date *date2) {
273 		assert(date1 != null);
274 		assert(date2 != null);
275 		return mg_date_days(date1) == mg_date_days(date2);
276 	}
277 
278 	/// Compares two `mg_time`s.
279 	static bool areTimesEqual(const mg_time *time1, const mg_time *time2) {
280 		assert(time1 != null);
281 		assert(time2 != null);
282 		return mg_time_nanoseconds(time1) == mg_time_nanoseconds(time2) &&
283 			mg_time_tz_offset_seconds(time1) == mg_time_tz_offset_seconds(time2);
284 	}
285 
286 	/// Compares two `mg_local_time`s.
287 	static bool areLocalTimesEqual(const mg_local_time *local_time1,
288 			const mg_local_time *local_time2) {
289 		assert(local_time1 != null);
290 		assert(local_time2 != null);
291 		return mg_local_time_nanoseconds(local_time1) ==
292 			mg_local_time_nanoseconds(local_time2);
293 	}
294 
295 	/// Compares two `mg_date_time`s.
296 	static bool areDateTimesEqual(const mg_date_time *date_time1,
297 			const mg_date_time *date_time2) {
298 		assert(date_time1 != null);
299 		assert(date_time2 != null);
300 		return mg_date_time_seconds(date_time1) == mg_date_time_seconds(date_time2) &&
301 			mg_date_time_nanoseconds(date_time1) ==
302 			mg_date_time_nanoseconds(date_time2) &&
303 			mg_date_time_tz_offset_minutes(date_time1) ==
304 			mg_date_time_tz_offset_minutes(date_time2);
305 	}
306 
307 	/// Compares two `mg_date_time_zone`s.
308 	static bool areDateTimeZoneIdsEqual(
309 			const mg_date_time_zone_id *date_time_zone_id1,
310 			const mg_date_time_zone_id *date_time_zone_id2) {
311 		assert(date_time_zone_id1 != null);
312 		assert(date_time_zone_id2 != null);
313 		return mg_date_time_zone_id_seconds(date_time_zone_id1) ==
314 			mg_date_time_zone_id_seconds(date_time_zone_id2) &&
315 			mg_date_time_zone_id_nanoseconds(date_time_zone_id1) ==
316 			mg_date_time_zone_id_nanoseconds(date_time_zone_id2) &&
317 			mg_date_time_zone_id_tz_id(date_time_zone_id1) ==
318 			mg_date_time_zone_id_tz_id(date_time_zone_id2);
319 	}
320 
321 	/// Compares two `mg_local_date_time`s.
322 	static bool areLocalDateTimesEqual(const mg_local_date_time *local_date_time1,
323 			const mg_local_date_time *local_date_time2) {
324 		assert(local_date_time1 != null);
325 		assert(local_date_time2 != null);
326 		return mg_local_date_time_seconds(local_date_time1) ==
327 			mg_local_date_time_seconds(local_date_time2) &&
328 			mg_local_date_time_nanoseconds(local_date_time1) ==
329 			mg_local_date_time_nanoseconds(local_date_time2);
330 	}
331 
332 	/// Compares two `mg_duration`s.
333 	static bool areDurationsEqual(const mg_duration *duration1,
334 			const mg_duration *duration2) {
335 		assert(duration1 != null);
336 		assert(duration2 != null);
337 		return mg_duration_months(duration1) == mg_duration_months(duration2) &&
338 			mg_duration_days(duration1) == mg_duration_days(duration2) &&
339 			mg_duration_seconds(duration1) == mg_duration_seconds(duration2) &&
340 			mg_duration_nanoseconds(duration1) ==
341 			mg_duration_nanoseconds(duration2);
342 	}
343 
344 	/// Compares two `mg_point_2d`s.
345 	static bool arePoint2dsEqual(const mg_point_2d *point_2d1,
346 			const mg_point_2d *point_2d2) {
347 		assert(point_2d1 != null);
348 		assert(point_2d2 != null);
349 		return mg_point_2d_srid(point_2d1) == mg_point_2d_srid(point_2d2) &&
350 			mg_point_2d_x(point_2d1) == mg_point_2d_x(point_2d2) &&
351 			mg_point_2d_y(point_2d1) == mg_point_2d_y(point_2d2);
352 	}
353 
354 	/// Compares two `mg_point_3d`s.
355 	static bool arePoint3dsEqual(const mg_point_3d *point_3d1,
356 			const mg_point_3d *point_3d2) {
357 		assert(point_3d1 != null);
358 		assert(point_3d2 != null);
359 		return mg_point_3d_srid(point_3d1) == mg_point_3d_srid(point_3d2) &&
360 			mg_point_3d_x(point_3d1) == mg_point_3d_x(point_3d2) &&
361 			mg_point_3d_y(point_3d1) == mg_point_3d_y(point_3d2) &&
362 			mg_point_3d_z(point_3d1) == mg_point_3d_z(point_3d2);
363 	}
364 }