1 module dpq2.types.from_bson; 2 3 @trusted: 4 5 import dpq2; 6 import vibe.data.bson; 7 import std.bitmanip: nativeToBigEndian; 8 9 /// Default type will be used for NULL value and for array without detected type 10 @property Value bsonToValue(Bson v) 11 { 12 if(v.type == Bson.Type.array) 13 return bsonArrayToValue(v); 14 else 15 return bsonValueToValue(v); 16 } 17 18 private: 19 20 Value bsonValueToValue(Bson v) 21 { 22 Value ret; 23 24 with(Bson.Type) 25 switch(v.type) 26 { 27 case null_: 28 ret = Value(ValueFormat.BINARY, OidType.Unknown); 29 break; 30 31 case int_: 32 ret = v.get!int.toValue; 33 break; 34 35 case long_: 36 ret = v.get!long.toValue; 37 break; 38 39 case double_: 40 ret = v.get!double.toValue; 41 break; 42 43 case Bson.Type..string: 44 ret = v.get!(immutable(char)[]).toValue; 45 break; 46 47 default: 48 throw new AnswerConvException( 49 ConvExceptionType.NOT_IMPLEMENTED, 50 "Format "~v.type.to!(immutable(char)[])~" doesn't supported by Bson to Value converter", 51 __FILE__, __LINE__ 52 ); 53 } 54 55 return ret; 56 } 57 58 unittest 59 { 60 { 61 Value v1 = bsonToValue(Bson(123)); 62 Value v2 = (123).toValue; 63 64 assert(v1.as!int == v2.as!int); 65 } 66 67 { 68 Value v1 = bsonToValue(Bson("Test string")); 69 Value v2 = ("Test string").toValue; 70 71 assert(v1.as!string == v2.as!string); 72 } 73 } 74 75 Value bsonArrayToValue(ref Bson bsonArr) 76 { 77 ubyte[] nullValue() pure 78 { 79 ubyte[] ret = [0xff, 0xff, 0xff, 0xff]; //NULL magic number 80 return ret; 81 } 82 83 ubyte[] rawValue(Value v) pure 84 { 85 if(v.isNull) 86 { 87 return nullValue(); 88 } 89 else 90 { 91 return v._data.length.to!uint.nativeToBigEndian ~ v._data; 92 } 93 } 94 95 ArrayProperties ap; 96 ubyte[] rawValues; 97 98 void recursive(ref Bson bsonArr, int dimension) 99 { 100 if(dimension == ap.dimsSize.length) 101 { 102 ap.dimsSize ~= bsonArr.length.to!int; 103 } 104 else 105 { 106 if(ap.dimsSize[dimension] != bsonArr.length) 107 throw new AnswerConvException(ConvExceptionType.NOT_ARRAY, "Jagged arrays are unsupported", __FILE__, __LINE__); 108 } 109 110 foreach(bElem; bsonArr) 111 { 112 ap.nElems++; 113 114 switch(bElem.type) 115 { 116 case Bson.Type.array: 117 recursive(bElem, dimension + 1); 118 break; 119 120 case Bson.Type.null_: 121 rawValues ~= nullValue(); 122 break; 123 124 default: 125 Value v = bsonValueToValue(bElem); 126 127 if(ap.OID == OidType.Unknown) 128 { 129 ap.OID = v.oidType; 130 } 131 else 132 { 133 if(ap.OID != v.oidType) 134 throw new AnswerConvException( 135 ConvExceptionType.NOT_ARRAY, 136 "Bson (which used for creating "~ap.OID.to!string~" array) also contains value of type "~v.oidType.to!string, 137 __FILE__, __LINE__ 138 ); 139 } 140 141 rawValues ~= rawValue(v); 142 } 143 } 144 } 145 146 recursive(bsonArr, 0); 147 148 // If array empty or contains only NULL values this allows to read it using ::text cast 149 if(ap.OID == OidType.Unknown) ap.OID = OidType.Text; 150 151 ArrayHeader_net h; 152 h.ndims = nativeToBigEndian(ap.dimsSize.length.to!int); 153 h.OID = nativeToBigEndian(ap.OID.to!Oid); 154 155 ubyte[] ret; 156 ret ~= (cast(ubyte*) &h)[0 .. h.sizeof]; 157 158 foreach(i; 0 .. ap.dimsSize.length) 159 { 160 Dim_net dim; 161 dim.dim_size = nativeToBigEndian(ap.dimsSize[i]); 162 dim.lbound = nativeToBigEndian!int(1); 163 164 ret ~= (cast(ubyte*) &dim)[0 .. dim.sizeof]; 165 } 166 167 ret ~= rawValues; 168 169 return Value(ret, ap.OID.oidType2arrayType, false, ValueFormat.BINARY); 170 } 171 172 unittest 173 { 174 { 175 Bson bsonArray = Bson( 176 [Bson(123), Bson(155), Bson(null), Bson(0), Bson(null)] 177 ); 178 179 Value v = bsonToValue(bsonArray); 180 181 assert(v.isSupportedArray); 182 assert(v.toBson == bsonArray); 183 } 184 185 { 186 Bson bsonArray = Bson([ 187 Bson([Bson(123), Bson(155), Bson(null)]), 188 Bson([Bson(0), Bson(null), Bson(155)]) 189 ]); 190 191 Value v = bsonToValue(bsonArray); 192 193 assert(v.isSupportedArray); 194 assert(v.toBson == bsonArray); 195 } 196 197 { 198 Bson bsonArray = Bson([ 199 Bson([Bson(123), Bson(155)]), 200 Bson([Bson(0)]) 201 ]); 202 203 bool exceptionFlag = false; 204 205 try 206 bsonToValue(bsonArray); 207 catch(AnswerConvException e) 208 { 209 if(e.type == ConvExceptionType.NOT_ARRAY) 210 exceptionFlag = true; 211 } 212 213 assert(exceptionFlag); 214 } 215 } 216 217 OidType oidType2arrayType(OidType type) 218 { 219 with(OidType) 220 switch(type) 221 { 222 case Text: 223 return TextArray; 224 225 case Int2: 226 return Int2Array; 227 228 case Int4: 229 return Int4Array; 230 231 case Int8: 232 return Int8Array; 233 234 case Float4: 235 return Float4Array; 236 237 case Float8: 238 return Float8Array; 239 240 default: 241 throw new AnswerConvException( // TODO: rename it to ValueConvException and move to value.d 242 ConvExceptionType.NOT_IMPLEMENTED, 243 "Format "~type.to!(immutable(char)[])~" doesn't supported by Bson array to Value converter", 244 __FILE__, __LINE__ 245 ); 246 } 247 }