1 /// 2 module dpq2.conv.from_bson; 3 4 import dpq2.value; 5 import dpq2.oids; 6 import dpq2.result: ArrayProperties, ArrayHeader_net, Dim_net; 7 import dpq2.conv.from_d_types; 8 import dpq2.conv.to_d_types; 9 import vibe.data.bson; 10 import std.bitmanip: nativeToBigEndian; 11 import std.conv: to; 12 13 /// Default type will be used for NULL value and for array without detected type 14 Value bsonToValue(Bson v, OidType defaultType = OidType.Undefined) 15 { 16 if(v.type == Bson.Type.array) 17 return bsonArrayToValue(v, defaultType); 18 else 19 return bsonValueToValue(v, defaultType); 20 } 21 22 private: 23 24 Value bsonValueToValue(Bson v, OidType defaultType) 25 { 26 Value ret; 27 28 with(Bson.Type) 29 switch(v.type) 30 { 31 case null_: 32 ret = Value(ValueFormat.BINARY, defaultType); 33 break; 34 35 case Bson.Type.object: 36 ret = v.toJson.toString.toValue; 37 ret.oidType = OidType.Json; 38 break; 39 40 case bool_: 41 ret = v.get!bool.toValue; 42 break; 43 44 case int_: 45 ret = v.get!int.toValue; 46 break; 47 48 case long_: 49 ret = v.get!long.toValue; 50 break; 51 52 case double_: 53 ret = v.get!double.toValue; 54 break; 55 56 case Bson.Type..string: 57 ret = v.get!(immutable(char)[]).toValue; 58 break; 59 60 default: 61 throw new ValueConvException( 62 ConvExceptionType.NOT_IMPLEMENTED, 63 "Format "~v.typeInternal.to!(immutable(char)[])~" doesn't supported by Bson to Value converter", 64 __FILE__, __LINE__ 65 ); 66 } 67 68 return ret; 69 } 70 71 //TODO: remove when vibe/data/bson.d removes deprecated types names 72 /// Bson.Type without deprecated items 73 package enum BsonTypeWODeprecated : ubyte 74 { 75 end = 0x00, /// End marker - should never occur explicitly 76 double_ = 0x01, /// A 64-bit floating point value 77 string = 0x02, /// A UTF-8 string 78 object = 0x03, /// An object aka. dictionary of string to Bson 79 array = 0x04, /// An array of BSON values 80 binData = 0x05, /// Raw binary data (ubyte[]) 81 undefined = 0x06, /// Deprecated 82 objectID = 0x07, /// BSON Object ID (96-bit) 83 bool_ = 0x08, /// Boolean value 84 date = 0x09, /// Date value (UTC) 85 null_ = 0x0A, /// Null value 86 regex = 0x0B, /// Regular expression 87 dbRef = 0x0C, /// Deprecated 88 code = 0x0D, /// JaveScript code 89 symbol = 0x0E, /// Symbol/variable name 90 codeWScope = 0x0F, /// JavaScript code with scope 91 int_ = 0x10, /// 32-bit integer 92 timestamp = 0x11, /// Timestamp value 93 long_ = 0x12, /// 64-bit integer 94 minKey = 0xff, /// Internal value 95 maxKey = 0x7f, /// Internal value 96 } 97 98 package BsonTypeWODeprecated typeInternal(in Bson v) 99 { 100 return cast(BsonTypeWODeprecated) v.type; 101 } 102 103 unittest 104 { 105 { 106 Value v1 = bsonToValue(Bson(123)); 107 Value v2 = (123).toValue; 108 109 assert(v1.as!int == v2.as!int); 110 } 111 112 { 113 Value v1 = bsonToValue(Bson("Test string")); 114 Value v2 = ("Test string").toValue; 115 116 assert(v1.as!string == v2.as!string); 117 } 118 119 { 120 Value t = bsonToValue(Bson(true)); 121 Value f = bsonToValue(Bson(false)); 122 123 assert(t.as!bool == true); 124 assert(f.as!bool == false); 125 } 126 } 127 128 Value bsonArrayToValue(ref Bson bsonArr, OidType defaultType) 129 { 130 ubyte[] nullValue() pure 131 { 132 ubyte[] ret = [0xff, 0xff, 0xff, 0xff]; //NULL magic number 133 return ret; 134 } 135 136 ubyte[] rawValue(Value v) pure 137 { 138 if(v.isNull) 139 { 140 return nullValue(); 141 } 142 else 143 { 144 return v._data.length.to!uint.nativeToBigEndian ~ v._data; 145 } 146 } 147 148 ArrayProperties ap; 149 ubyte[] rawValues; 150 151 void recursive(ref Bson bsonArr, int dimension) 152 { 153 if(dimension == ap.dimsSize.length) 154 { 155 ap.dimsSize ~= bsonArr.length.to!int; 156 } 157 else 158 { 159 if(ap.dimsSize[dimension] != bsonArr.length) 160 throw new ValueConvException(ConvExceptionType.NOT_ARRAY, "Jagged arrays are unsupported", __FILE__, __LINE__); 161 } 162 163 foreach(bElem; bsonArr) 164 { 165 ap.nElems++; 166 167 switch(bElem.type) 168 { 169 case Bson.Type.array: 170 recursive(bElem, dimension + 1); 171 break; 172 173 case Bson.Type.null_: 174 rawValues ~= nullValue(); 175 break; 176 177 default: 178 Value v = bsonValueToValue(bElem, OidType.Undefined); 179 180 if(ap.OID == OidType.Undefined) 181 { 182 ap.OID = v.oidType; 183 } 184 else 185 { 186 if(ap.OID != v.oidType) 187 throw new ValueConvException( 188 ConvExceptionType.NOT_ARRAY, 189 "Bson (which used for creating "~ap.OID.to!string~" array) also contains value of type "~v.oidType.to!string, 190 __FILE__, __LINE__ 191 ); 192 } 193 194 rawValues ~= rawValue(v); 195 } 196 } 197 } 198 199 recursive(bsonArr, 0); 200 201 if(ap.OID == OidType.Undefined) ap.OID = defaultType.oidConvTo!"element"; 202 203 ArrayHeader_net h; 204 h.ndims = nativeToBigEndian(ap.dimsSize.length.to!int); 205 h.OID = nativeToBigEndian(ap.OID.to!Oid); 206 207 ubyte[] ret; 208 ret ~= (cast(ubyte*) &h)[0 .. h.sizeof]; 209 210 foreach(i; 0 .. ap.dimsSize.length) 211 { 212 Dim_net dim; 213 dim.dim_size = nativeToBigEndian(ap.dimsSize[i]); 214 dim.lbound = nativeToBigEndian!int(1); 215 216 ret ~= (cast(ubyte*) &dim)[0 .. dim.sizeof]; 217 } 218 219 ret ~= rawValues; 220 221 return Value(cast(immutable) ret, ap.OID.oidConvTo!"array", false, ValueFormat.BINARY); 222 } 223 224 unittest 225 { 226 import dpq2.conv.to_bson; 227 228 { 229 Bson bsonArray = Bson( 230 [Bson(123), Bson(155), Bson(null), Bson(0), Bson(null)] 231 ); 232 233 Value v = bsonToValue(bsonArray); 234 235 assert(v.isSupportedArray); 236 assert(v.as!Bson == bsonArray); 237 } 238 239 { 240 Bson bsonArray = Bson([ 241 Bson([Bson(123), Bson(155), Bson(null)]), 242 Bson([Bson(0), Bson(null), Bson(155)]) 243 ]); 244 245 Value v = bsonToValue(bsonArray); 246 247 assert(v.isSupportedArray); 248 assert(v.as!Bson == bsonArray); 249 } 250 251 { 252 Bson bsonArray = Bson([ 253 Bson([Bson(123), Bson(155)]), 254 Bson([Bson(0)]) 255 ]); 256 257 bool exceptionFlag = false; 258 259 try 260 bsonToValue(bsonArray); 261 catch(ValueConvException e) 262 { 263 if(e.type == ConvExceptionType.NOT_ARRAY) 264 exceptionFlag = true; 265 } 266 267 assert(exceptionFlag); 268 } 269 }