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 }