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 }