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