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 }