1 module telega.serialization;
2 
3 import std.meta : AliasSeq, staticIndexOf;
4 import asdf : Asdf, serializeValue, serializeToAsdf, serializeToJson, serializedAs, parseJson;
5 
6 version (unittest)
7 {
8     import telega.test : assertEquals;
9 }
10 
11 string serializeToJsonString(T)(T value)
12 {
13     import std.conv : to;
14 
15     Asdf asdf = serializeToAsdf(value);
16     removeNulledNodes(asdf);
17 
18     return  asdf.to!string;
19 }
20 
21 unittest
22 {
23     alias FewTypes = AliasSeq!(int, string);
24 
25     struct S
26     {
27         JsonableAlgebraicProxy!FewTypes a;
28     }
29 
30     S s;
31     s.a = 42;
32 
33     s.serializeToJson()
34         .assertEquals(`{"a":42}`);
35 
36     s.a = "123";
37     s.serializeToJson()
38         .assertEquals(`{"a":"123"}`);
39 }
40 
41 /**
42  * Proxy struct for serializing string enums as their values, not key names
43  */
44 @serializedAs!string
45 struct SerializableEnumProxy(T)
46     if (is(T : string))
47 {
48     T e;
49 
50     this(T e)
51     {
52         this.e = e;
53     }
54 
55     string toString()
56     {
57         return cast(string)e;
58     }
59 }
60 
61 version (unittest)
62 {
63     @serializedAs!(SerializableEnumProxy!E)
64     static enum E : string
65     {
66         Val1 = "value_1"
67     }
68 }
69 
70 unittest
71 {
72     E e = E.Val1;
73 
74     assertEquals(e.serializeToJson(), `"value_1"`);
75 }
76 
77 void removeNulledNodes(ref Asdf a)
78 {
79     if (a.kind == Asdf.Kind.null_) {
80         a.remove();
81     } else if (a.kind == Asdf.Kind.array) {
82         foreach (v; a.byElement) {
83             removeNulledNodes(v);
84         }
85     } else if (a.kind == Asdf.Kind.object) {
86         foreach (kv; a.byKeyValue) {
87             removeNulledNodes(kv.value);
88         }
89     }
90 }
91 
92 unittest
93 {
94     import std.conv: to;
95 
96     string jsonWithNulls = `{
97         "chat_id":"100000001",
98         "text":"o",
99         "reply_markup":{
100             "keyboard":[
101                 [
102                     {"text":"First Button","request_contact":null,"request_location":null},
103                     {"text":"Second Button","request_contact":null,"request_location":null}
104                 ],
105                 [
106                     {"text":"Ask location","request_contact":false,"request_location":true}
107                 ],
108                 [
109                     {"text":"Remove Keyboard","request_contact":null,"request_location":null}
110                 ]
111             ],
112             "resize_keyboard":false,
113             "selective":false,
114             "one_time_keyboard":false
115         }
116     }`;
117     const string cleanJson = `{
118         "chat_id":"100000001",
119         "text":"o",
120         "reply_markup":{
121             "keyboard":[
122                 [
123                     {"text":"First Button"},
124                     {"text":"Second Button"}
125                 ],
126                 [
127                     {"text":"Ask location","request_contact":false,"request_location":true}
128                 ],
129                 [
130                     {"text":"Remove Keyboard"}
131                 ]
132             ],
133             "resize_keyboard":false,
134             "selective":false,
135             "one_time_keyboard":false
136         }
137     }`;
138 
139     Asdf asdf = jsonWithNulls.parseJson();
140 
141     removeNulledNodes(asdf);
142 
143     asdf.to!string
144         .assertEquals(cleanJson.parseJson.to!string);
145 }
146 
147 struct JsonableAlgebraicProxy(Typelist ...)
148 {
149     import std.variant : Algebraic;
150 
151     private Algebraic!Typelist value;
152 
153     this(T)(T value)
154         if (staticIndexOf!(T, Typelist) >= 0)
155     {
156         opAssign(value);
157     }
158 
159     void opAssign(T)(T value)
160         if (staticIndexOf!(T, Typelist) >= 0)
161     {
162         this.value = value;
163     }
164 
165     static Algebraic!Typelist deserialize(Asdf data)
166     {
167         assert(false, "Deserialization of a value is not implemented.");
168     }
169 
170     void serialize(S)(ref S serializer)
171     {
172         if (!value.hasValue) {
173             serializer.putValue(null);
174 
175             return;
176         }
177 
178         static foreach (T; Typelist) {
179             if (value.type == typeid(T)) {
180                 T result = cast(T)value.get!T;
181 
182                 serializer.serializeValue(result);
183             }
184         }
185     }
186 }
187 
188 unittest
189 {
190     struct A {
191         int val;
192     }
193 
194     alias AProxy = JsonableAlgebraicProxy!A;
195 
196     AProxy[] aelements;
197 
198     assert(__traits(compiles, aelements ~= cast(AProxy)A(3)));
199 }