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.type.to!(immutable(char)[])~" doesn't supported by Bson to Value converter",
64                     __FILE__, __LINE__
65                 );
66     }
67 
68     return ret;
69 }
70 
71 unittest
72 {
73     {
74         Value v1 = bsonToValue(Bson(123));
75         Value v2 = (123).toValue;
76 
77         assert(v1.as!int == v2.as!int);
78     }
79 
80     {
81         Value v1 = bsonToValue(Bson("Test string"));
82         Value v2 = ("Test string").toValue;
83 
84         assert(v1.as!string == v2.as!string);
85     }
86 
87     {
88         Value t = bsonToValue(Bson(true));
89         Value f = bsonToValue(Bson(false));
90 
91         assert(t.as!bool == true);
92         assert(f.as!bool == false);
93     }
94 }
95 
96 Value bsonArrayToValue(ref Bson bsonArr, OidType defaultType)
97 {
98     ubyte[] nullValue() pure
99     {
100         ubyte[] ret = [0xff, 0xff, 0xff, 0xff]; //NULL magic number
101         return ret;
102     }
103 
104     ubyte[] rawValue(Value v) pure
105     {
106         if(v.isNull)
107         {
108             return nullValue();
109         }
110         else
111         {
112             return v._data.length.to!uint.nativeToBigEndian ~ v._data;
113         }
114     }
115 
116     ArrayProperties ap;
117     ubyte[] rawValues;
118 
119     void recursive(ref Bson bsonArr, int dimension)
120     {
121         if(dimension == ap.dimsSize.length)
122         {
123             ap.dimsSize ~= bsonArr.length.to!int;
124         }
125         else
126         {
127             if(ap.dimsSize[dimension] != bsonArr.length)
128                 throw new ValueConvException(ConvExceptionType.NOT_ARRAY, "Jagged arrays are unsupported", __FILE__, __LINE__);
129         }
130 
131         foreach(bElem; bsonArr)
132         {
133             ap.nElems++;
134 
135             switch(bElem.type)
136             {
137                 case Bson.Type.array:
138                     recursive(bElem, dimension + 1);
139                     break;
140 
141                 case Bson.Type.null_:
142                     rawValues ~= nullValue();
143                     break;
144 
145                 default:
146                     Value v = bsonValueToValue(bElem, OidType.Undefined);
147 
148                     if(ap.OID == OidType.Undefined)
149                     {
150                         ap.OID = v.oidType;
151                     }
152                     else
153                     {
154                         if(ap.OID != v.oidType)
155                             throw new ValueConvException(
156                                     ConvExceptionType.NOT_ARRAY,
157                                     "Bson (which used for creating "~ap.OID.to!string~" array) also contains value of type "~v.oidType.to!string,
158                                     __FILE__, __LINE__
159                                 );
160                     }
161 
162                     rawValues ~= rawValue(v);
163             }
164         }
165     }
166 
167     recursive(bsonArr, 0);
168 
169     if(ap.OID == OidType.Undefined) ap.OID = defaultType.oidConvTo!"element";
170 
171     ArrayHeader_net h;
172     h.ndims = nativeToBigEndian(ap.dimsSize.length.to!int);
173     h.OID = nativeToBigEndian(ap.OID.to!Oid);
174 
175     ubyte[] ret;
176     ret ~= (cast(ubyte*) &h)[0 .. h.sizeof];
177 
178     foreach(i; 0 .. ap.dimsSize.length)
179     {
180         Dim_net dim;
181         dim.dim_size = nativeToBigEndian(ap.dimsSize[i]);
182         dim.lbound = nativeToBigEndian!int(1);
183 
184         ret ~= (cast(ubyte*) &dim)[0 .. dim.sizeof];
185     }
186 
187     ret ~= rawValues;
188 
189     return Value(cast(immutable) ret, ap.OID.oidConvTo!"array", false, ValueFormat.BINARY);
190 }
191 
192 unittest
193 {
194     import dpq2.conv.to_bson;
195 
196     {
197         Bson bsonArray = Bson(
198             [Bson(123), Bson(155), Bson(null), Bson(0), Bson(null)]
199         );
200 
201         Value v = bsonToValue(bsonArray);
202 
203         assert(v.isSupportedArray);
204         assert(v.as!Bson == bsonArray);
205     }
206 
207     {
208         Bson bsonArray = Bson([
209             Bson([Bson(123), Bson(155), Bson(null)]),
210             Bson([Bson(0), Bson(null), Bson(155)])
211         ]);
212 
213         Value v = bsonToValue(bsonArray);
214 
215         assert(v.isSupportedArray);
216         assert(v.as!Bson == bsonArray);
217     }
218 
219     {
220         Bson bsonArray = Bson([
221             Bson([Bson(123), Bson(155)]),
222             Bson([Bson(0)])
223         ]);
224 
225         bool exceptionFlag = false;
226 
227         try
228             bsonToValue(bsonArray);
229         catch(ValueConvException e)
230         {
231             if(e.type == ConvExceptionType.NOT_ARRAY)
232                 exceptionFlag = true;
233         }
234 
235         assert(exceptionFlag);
236     }
237 }