1 /*
2  * Medea: Modern JSON for D
3  */
4 module medea;
5 
6 import std.json;
7 import std.traits;
8 import std.conv : text;
9 
10 abstract class Value {
11     public: 
12         JSONValue toJSONValue();
13 }
14 
15 string toJSONString(Value value, bool pretty = false) {
16     JSONValue jval = value.toJSONValue();
17     return (&jval).toJSON(pretty);
18 }
19 
20 abstract class NumberValue : Value {
21     
22 }
23 
24 final class IntegerValue : NumberValue {
25     private:
26         long _n;
27     public:
28         this(in long n) {
29             _n = n;
30         }
31 
32         this() {
33             this(0);
34         }
35 
36         override JSONValue toJSONValue() {
37             JSONValue v;
38             v.integer = _n;
39             return v;
40         }
41         @property long number() {
42             return _n;
43         }
44 }
45 
46 final class UIntegerValue : NumberValue {
47     private:
48         ulong _n;
49     public:
50         this(in ulong n) {
51             _n = n;
52         }
53 
54         this() {
55             this(0);
56         }
57 
58         override JSONValue toJSONValue() {
59             JSONValue v;
60             v.uinteger = _n;
61             return v;
62         }
63         @property ulong number() {
64             return _n;
65         }
66 }
67 
68 final class FloatValue : NumberValue {
69     private:
70         real _n;
71     public:
72         this(in real n) {
73             _n = n;
74         }
75 
76         this() {
77             this(0.0f);
78         }
79 
80         override JSONValue toJSONValue() {
81             JSONValue v;
82             v.floating = _n;
83             return v;
84         }
85         @property real number() {
86             return _n;
87         }
88 }
89 
90 final class BoolValue : Value {
91     private:
92         bool _b;
93     public:
94         this(in bool b) {
95             _b = b;
96         }
97 
98         this() {
99             this(false);
100         }
101 
102         override JSONValue toJSONValue() {
103             JSONValue v;
104             v = _b;
105             return v;
106         }
107         @property bool boolean() {
108             return _b;
109         }
110 }
111 
112 final class StringValue : Value {
113     private:
114         string _str;
115 
116     public:
117         this(in string str) {
118             _str = str;
119         }
120 
121         this() {
122             this("");
123         }
124 
125         override JSONValue toJSONValue() {
126             JSONValue v;
127             v.str = _str;
128             return v;
129         }
130 
131         @property string text() {
132             return _str;
133         }
134 }
135 
136 final class ObjectValue : Value {
137     private:
138         Value[string] _values;
139 
140     public:
141         this(Value[string] values) {
142             _values = values;
143         }
144 
145         this(JSONValue[string] values) {
146             foreach(string name, ref JSONValue v; values) {
147                 this._values[name] = v.wrap();
148             }
149         }
150 
151         this() {
152 
153         }
154 
155         override JSONValue toJSONValue() {
156             JSONValue v;
157             JSONValue[string] props;
158             foreach(string name, Value v; _values) {
159                 props[name] = v.toJSONValue();
160             }
161             v.object = props;
162             return v;
163         }
164 
165         @property Value[string] properties() {
166             return _values;
167         }
168 
169         auto opBinaryRight(string op : "in")(string k) const
170         {
171             return k in _values;
172         }
173 
174         Value opIndex(string k)
175         {
176             if(k !in this) {
177                 return null;
178             }
179             return _values[k];
180         }
181 
182         Value opIndexAssign(Value v, string k) {
183             _values[k] = v;
184             return v;
185         }
186 }
187 
188 final class ArrayValue : Value {
189     private:
190         Value[] _values;
191 
192     public:
193         this(Value[] values) {
194             _values = values;
195         }
196 
197         this(JSONValue[] values) {
198             foreach(ref JSONValue v; values) {
199                 this._values ~= v.wrap();
200             }
201         }
202 
203         this() {
204 
205         }
206 
207         override JSONValue toJSONValue() {
208             JSONValue v;
209             JSONValue[] items;
210             foreach(ref Value i; _values) {
211                 items ~= i.toJSONValue();
212             }
213             v.array = items;
214             return v;
215         }
216 
217         @property Value[] items() {
218             return _values;
219         }
220 
221         Value opIndex(size_t i)
222         {
223             if(i > _values.length -1) {
224                 return null;
225             }
226             return _values[i];
227         }
228         Value opIndexAssign(Value v, size_t i) {
229             _values[i] = v;
230             return v;
231         }
232 }
233 
234 final class NullValue : Value {
235 
236     override JSONValue toJSONValue() {
237         JSONValue v = null;
238         return v;
239     }
240 }
241 
242 Value wrap(JSONValue value) {
243     if(value == JSONValue.init) {
244         return null;
245     }
246     switch(value.type) {
247         case JSON_TYPE.STRING: {
248             return new StringValue(value.str);
249         }
250         case JSON_TYPE.INTEGER: {
251             return new IntegerValue(value.integer);
252         }
253         case JSON_TYPE.UINTEGER: {
254             return new UIntegerValue(value.uinteger);
255         }
256         case JSON_TYPE.FLOAT: {
257             return new FloatValue(value.floating);
258         }
259         case JSON_TYPE.OBJECT: {
260             return new ObjectValue(value.object);
261         }
262         case JSON_TYPE.ARRAY: {
263             return new ArrayValue(value.array);
264         }
265         case JSON_TYPE.NULL: {
266             return new NullValue();
267         }
268         case JSON_TYPE.TRUE: {
269             return new BoolValue(true);
270         }
271         case JSON_TYPE.FALSE: {
272             return new BoolValue(false);
273         }
274         default:
275             throw new Exception("Unknown JSON_TYPE");
276     }
277 }
278 
279 Value parse(string doc) {
280     auto document = parseJSON(doc);  
281     return document.wrap();
282 }
283 
284 Value toValue(T)(T val) {
285     static if(is(T: bool)) {
286         return new BoolValue(val);
287     }
288     else static if(is(T : long)) {
289         return new IntegerValue(val);
290     } else static if(is(T: string)) {
291         return new StringValue(val);
292     } else static if(isFloatingPoint!(T)) {
293         return new FloatValue(val);
294     } else static if(isArray!(T)) {
295         return new ArrayValue(val);
296     } else static if(is(T : Value[Key], Key)) {
297         static assert(is(Key : string), text("Key must be string but ", Key.stringof, " was given instead"));
298         return new ObjectValue(val);
299     } else {
300         static assert(false, text(`unable to convert type "`, T.stringof, `" to json Value`));
301     }
302 }
303