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 and the 12 // operation to be applied when doing an opCast/opEquals. 13 private static immutable enum mixinOps = [ 14 typeid(double): tuple(Type.Double, "mg_value_float(ptr_)"), 15 typeid(int): tuple(Type.Int, "to!int(mg_value_integer(ptr_))"), 16 typeid(long): tuple(Type.Int, "to!long(mg_value_integer(ptr_))"), 17 typeid(bool): tuple(Type.Bool, "to!bool(mg_value_bool(ptr_))"), 18 typeid(Node): tuple(Type.Node, "Node(mg_value_node(ptr_))"), 19 typeid(List): tuple(Type.List, "List(mg_value_list(ptr_))"), 20 typeid(Map): tuple(Type.Map, "Map(mg_value_map(ptr_))"), 21 typeid(Path): tuple(Type.Path, "Path(mg_value_path(ptr_))"), 22 typeid(Relationship): tuple(Type.Relationship, "Relationship(mg_value_relationship(ptr_))"), 23 typeid(UnboundRelationship): tuple(Type.UnboundRelationship, "UnboundRelationship(mg_value_unbound_relationship(ptr_))"), 24 typeid(string): tuple(Type.String, "Detail.convertString(mg_value_string(ptr_))"), 25 typeid(char[]): tuple(Type.String, "Detail.convertString(mg_value_string(ptr_))"), 26 typeid(Date): tuple(Type.Date, "Date(mg_value_date(ptr_))"), 27 typeid(LocalTime): tuple(Type.LocalTime, "LocalTime(mg_value_local_time(ptr_))"), 28 typeid(LocalDateTime): tuple(Type.LocalDateTime, "LocalDateTime(mg_value_local_date_time(ptr_))"), 29 typeid(Duration): tuple(Type.Duration, "Duration(mg_value_duration(ptr_))"), 30 typeid(Point2d): tuple(Type.Point2d, "Point2d(mg_value_point_2d(ptr_))"), 31 typeid(Point3d): tuple(Type.Point3d, "Point3d(mg_value_point_3d(ptr_))"), 32 ]; 33 34 /// A Bolt value, encapsulating all other values. 35 struct Value { 36 /// Copy constructor. 37 @nogc this(const ref Value rhs) { 38 this(rhs.ptr_); 39 } 40 41 /// Cast this value to type `T`. 42 /// Note: The code asserts that the current value holds a representation of type `T`. 43 auto opCast(T)() const { 44 assert(type == mixinOps[typeid(T)][0], "expected " ~ to!string(type) ~ " got " ~ T.stringof); 45 return mixin(mixinOps[typeid(T)][1]); 46 } 47 48 /// Comparison operator for type `T`. 49 /// Note: The code asserts that the current value holds a representation of type `T`. 50 bool opEquals(T)(const T val) const { 51 assert(type == mixinOps[typeid(T)][0], "expected " ~ to!string(type) ~ " got " ~ T.stringof); 52 return mixin(mixinOps[typeid(T)][1]) == val; 53 } 54 55 /// Comparison operator for another `Value`. 56 @nogc bool opEquals(const Value other) const { 57 return Detail.areValuesEqual(ptr_, other.ptr_); 58 } 59 60 /// Return the hash code for this value. 61 size_t toHash() const nothrow @safe { 62 return cast(ulong)ptr_; 63 } 64 65 /// Return this value as a string. 66 /// If the value held is not of type `Type.String`, then it will 67 /// be first converted into the appropriate string representation. 68 string toString() const { 69 final switch (type) { 70 case Type.Null: return "(null)"; 71 case Type.Double: return to!string(mg_value_float(ptr_)); 72 case Type.Bool: return to!string(mg_value_bool(ptr_)); 73 case Type.Int: return to!string(mg_value_integer(ptr_)); 74 case Type.String: return Detail.convertString(mg_value_string(ptr_)); 75 case Type.Relationship: return to!string(Relationship(mg_value_relationship(ptr_))); 76 case Type.UnboundRelationship: return to!string(UnboundRelationship(mg_value_unbound_relationship(ptr_))); 77 case Type.Node: return to!string(Node(mg_value_node(ptr_))); 78 case Type.List: return to!string(List(mg_value_list(ptr_))); 79 case Type.Map: return to!string(Map(mg_value_map(ptr_))); 80 case Type.Path: return to!string(Path(mg_value_path(ptr_))); 81 case Type.Date: return to!string(Date(mg_value_date(ptr_))); 82 case Type.LocalTime: return to!string(LocalTime(mg_value_local_time(ptr_))); 83 case Type.LocalDateTime: return to!string(LocalDateTime(mg_value_local_date_time(ptr_))); 84 case Type.Duration: return to!string(Duration(mg_value_duration(ptr_))); 85 case Type.Point2d: return to!string(Point2d(mg_value_point_2d(ptr_))); 86 case Type.Point3d: return to!string(Point3d(mg_value_point_3d(ptr_))); 87 } 88 } 89 90 /// Return the type of value being held. 91 @nogc @property Type type() const { 92 return Detail.convertType(mg_value_get_type(ptr_)); 93 } 94 95 package: 96 /// Create a Value using the given `mg_value`. 97 @nogc this(const mg_value *p) { 98 assert(p != null); 99 ptr_ = p; 100 } 101 102 /// Returns internal `mg_value` pointer. 103 @nogc auto ptr() inout { return ptr_; } 104 105 private: 106 const mg_value *ptr_; 107 } // struct Value 108 109 // value tests 110 unittest { 111 import testutils : connectContainer; 112 import memgraph.local_date_time; 113 import std.conv : to; 114 115 auto client = connectContainer(); 116 assert(client); 117 118 auto result = client.execute(`return ["Zdravo, svijete!", 42];`); 119 assert(result, client.error); 120 foreach (r; result) { 121 assert(r.length == 1); 122 assert(r[0].type() == Type.List); 123 124 auto l = to!List(r[0]); 125 126 assert(l.length == 2); 127 128 assert(l[0].type() == Type.String); 129 assert(l[1].type() == Type.Int); 130 131 assert(to!string(l[0]) == "Zdravo, svijete!"); 132 assert(to!int(l[1]) == 42); 133 } 134 } 135 136 unittest { 137 auto nullValue = Value(mg_value_make_null()); 138 assert(to!string(nullValue) == "(null)"); 139 assert(cast(ulong)nullValue.ptr == nullValue.toHash); 140 }