1 module google.protobuf.struct_;
2 
3 import std.json : JSONValue;
4 import google.protobuf;
5 
6 class Struct
7 {
8     @Proto(1) Value[string] fields = protoDefaultValue!(Value[string]);
9 
10     this()
11     {
12     }
13 
14     this(JSONValue value)
15     {
16         fromJSONValue(value);
17     }
18 
19     JSONValue toJSONValue()()
20     {
21         import std.array : assocArray;
22         import std.algorithm : map;
23         import std.typecons : tuple;
24 
25         return JSONValue(fields.byKeyValue.map!(a => tuple(a.key, a.value.toJSONValue)).assocArray);
26     }
27 
28     auto fromJSONValue()(JSONValue value)
29     {
30         import std.array : assocArray;
31         import std.algorithm : map;
32         import std.exception : enforce;
33         import std.json : JSONType;
34         import std.typecons : tuple;
35 
36         if (value.type == JSONType.null_)
37         {
38             fields = protoDefaultValue!(Value[string]);
39             return this;
40         }
41 
42         enforce!ProtobufException(value.type == JSONType.object, "JSON object expected");
43         fields = value.object.byKeyValue.map!(a => tuple(a.key, new Value(a.value))).assocArray;
44 
45         return this;
46     }
47 }
48 
49 class Value
50 {
51     enum KindCase
52     {
53         kindNotSet = 0,
54         nullValue = 1,
55         numberValue = 2,
56         stringValue = 3,
57         boolValue = 4,
58         structValue = 5,
59         listValue = 6,
60     }
61     KindCase _kindCase = KindCase.kindNotSet;
62     @property KindCase kindCase() { return _kindCase; }
63     void clearKind() { _kindCase = KindCase.kindNotSet; }
64     @Oneof("_kindCase") union
65     {
66         @Proto(1) NullValue _nullValue = protoDefaultValue!NullValue; mixin(oneofAccessors!_nullValue);
67         @Proto(2) double _numberValue; mixin(oneofAccessors!_numberValue);
68         @Proto(3) string _stringValue; mixin(oneofAccessors!_stringValue);
69         @Proto(4) bool _boolValue; mixin(oneofAccessors!_boolValue);
70         @Proto(5) Struct _structValue; mixin(oneofAccessors!_structValue);
71         @Proto(6) ListValue _listValue; mixin(oneofAccessors!_listValue);
72     }
73 
74     this()
75     {
76     }
77 
78     this(JSONValue jsonValue)
79     {
80         fromJSONValue(jsonValue);
81     }
82 
83     override bool opEquals(Object o)
84     {
85         auto other = cast(Value) o;
86         if (other is null)
87             return false;
88 
89         if (kindCase != other.kindCase)
90             return false;
91 
92         final switch (kindCase)
93         {
94         case KindCase.kindNotSet:
95             return true;
96         case KindCase.nullValue:
97             return nullValue == other.nullValue;
98         case KindCase.numberValue:
99             return numberValue == other.numberValue;
100         case KindCase.stringValue:
101             return stringValue == other.stringValue;
102         case KindCase.boolValue:
103             return boolValue == other.boolValue;
104         case KindCase.structValue:
105             return structValue == other.structValue;
106         case KindCase.listValue:
107             return listValue == other.listValue;
108         }
109     }
110 
111     JSONValue toJSONValue()()
112     {
113         import std.array : array, assocArray;
114         import std.algorithm : map;
115         import std.typecons : tuple;
116 
117         final switch (kindCase)
118         {
119         case KindCase.kindNotSet:
120             return JSONValue(null);
121         case KindCase.nullValue:
122             return JSONValue(null);
123         case KindCase.numberValue:
124             return JSONValue(numberValue);
125         case KindCase.stringValue:
126             return JSONValue(stringValue);
127         case KindCase.boolValue:
128             return JSONValue(boolValue);
129         case KindCase.structValue:
130             return JSONValue(structValue.fields.byKeyValue.map!(a => tuple(a.key, a.value.toJSONValue)).assocArray);
131         case KindCase.listValue:
132             return JSONValue(listValue.values.map!(a => a.toJSONValue).array);
133         }
134     }
135 
136     auto fromJSONValue()(JSONValue jsonValue)
137     {
138         import std.array : array, assocArray;
139         import std.algorithm : map;
140         import std.json : JSONType;
141         import std.typecons : tuple;
142 
143         switch (jsonValue.type)
144         {
145         case JSONType.null_:
146             nullValue = NullValue.NULL_VALUE;
147             break;
148         case JSONType..string:
149             stringValue = jsonValue.str;
150             break;
151         case JSONType.integer:
152             numberValue = jsonValue.integer;
153             break;
154         case JSONType.uinteger:
155             numberValue = jsonValue.uinteger;
156             break;
157         case JSONType.float_:
158             numberValue = jsonValue.floating;
159             break;
160         case JSONType.object:
161             if (structValue is null)
162                 structValue = new Struct;
163             structValue.fields = jsonValue.object.byKeyValue.map!(a => tuple(a.key, new Value(a.value))).assocArray;
164             break;
165         case JSONType.array:
166             if (listValue is null)
167                 listValue = new ListValue;
168             listValue.values = jsonValue.array.map!(a => new Value(a)).array;
169             break;
170         case JSONType.true_:
171             boolValue = true;
172             break;
173         case JSONType.false_:
174             boolValue = false;
175             break;
176         default:
177             throw new ProtobufException("Unexpected JSON type");
178         }
179 
180         return this;
181     }
182 }
183 
184 enum NullValue
185 {
186     NULL_VALUE = 0,
187 }
188 
189 class ListValue
190 {
191     @Proto(1) Value[] values = protoDefaultValue!(Value[]);
192 
193     this()
194     {
195     }
196 
197     this(JSONValue value)
198     {
199         fromJSONValue(value);
200     }
201 
202     JSONValue toJSONValue()()
203     {
204         import std.array : array;
205         import std.algorithm : map;
206 
207         return JSONValue(values.map!(a => a.toJSONValue).array);
208     }
209 
210     auto fromJSONValue()(JSONValue value)
211     {
212         import std.array : array;
213         import std.algorithm : map;
214         import std.exception : enforce;
215         import std.json : JSONType;
216 
217         if (value.type == JSONType.null_)
218         {
219             values = protoDefaultValue!(Value[]);
220             return this;
221         }
222 
223         enforce!ProtobufException(value.type == JSONType.array, "JSON array expected");
224         values = value.array.map!(a => new Value(a)).array;
225 
226         return this;
227     }
228 }