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