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