1 module awebview.wrapper.jsvalue;
2 
3 import std.conv;
4 import std.format;
5 import std.range;
6 import std.utf;
7 import std.traits;
8 
9 import awebview.wrapper.cpp;
10 import awebview.wrapper.jsobject : JSObject;
11 import awebview.wrapper.jsarray : JSArray, JSArrayCpp;
12 import awebview.wrapper.jsarray;
13 import awebview.wrapper.jsobject;
14 import awebview.wrapper.webstring : WebString;
15 import awebview.wrapper.constants;
16 import awebview.wrapper.weakref;
17 
18 import carbon.templates;
19 import carbon.nonametype;
20 
21 
22 struct JSValue
23 {
24     this(bool b) nothrow @nogc { JSValueMember.ctor(this.cppObj!false, b); }
25     this(int v)  nothrow @nogc { JSValueMember.ctor(this.cppObj!false, v); }
26     this(double v)  nothrow @nogc { JSValueMember.ctor(this.cppObj!false, v); }
27 
28     this(const awebview.wrapper.cpp.WebString ws) nothrow @nogc
29     {
30         JSValueMember.ctor(this.cppObj!false, ws);
31     }
32 
33     this()(auto ref const WebString ws)  nothrow @nogc { this(ws.cppObj); }
34 
35     this(Char)(in Char[] str) nothrow @nogc
36     if(isSomeChar!Char) { this(WebString(str)); }
37 
38     this(const awebview.wrapper.cpp.JSObject jso) nothrow @nogc
39     {
40         JSValueMember.ctor(this.cppObj!false, jso);
41     }
42 
43     this()(auto ref const JSObject jo) nothrow @nogc { this(jo.cppObj); }
44 
45     this(const awebview.wrapper.cpp.JSArray jsarr) nothrow @nogc
46     {
47         JSValueMember.ctor(this.cppObj!false, jsarr);
48     }
49 
50     this()(auto ref const JSArray ja) nothrow @nogc { this(ja.cppObj); }
51 
52     this(const awebview.wrapper.cpp.JSValue v) nothrow @nogc
53     {
54         JSValueMember.ctor(this.cppObj!false, v);
55     }
56 
57     this(const JSValue v) nothrow @nogc
58     {
59         this(v.cppObj);
60     }
61 
62 
63     ~this() nothrow @nogc
64     {
65         if(this._field.value_ !is null){
66             JSValueMember.dtor(this.cppObj!false);
67             this._field.value_ = null;
68         }
69     }
70 
71 
72     this(this) nothrow @nogc
73     {
74         //import core.stdc.stdio;
75         //printf("on postblit %d\n", this._field.value_);
76         if(this._field.value_ !is null){
77             auto copy = JSValue(this.cppObj!false);
78             //printf("on postblit %d : %d\n", this._field.value_, copy._field.value_);
79             //fflush(stdout);
80             this._field.value_ = copy._field.value_;
81             copy._field.value_ = null;
82         }
83     }
84 
85 
86     void opAssign(const JSValue rhs) nothrow @nogc
87     {
88         JSValueMember.opAssign(this.cppObj, rhs.cppObj);
89     }
90 
91 
92     void opAssign(ref const JSValue rhs) nothrow @nogc
93     {
94         JSValueMember.opAssign(this.cppObj, rhs.cppObj);
95     }
96 
97 
98     void opAssign(T)(auto ref const T v)
99     if(is(typeof(JSValue(v))))
100     {
101         auto jv = JSValue(v);
102         this.opAssign(jv);
103     }
104 
105 
106     CppObj cppObj(bool withInitialize = true)() nothrow @trusted @property @nogc
107     {
108         CppObj ret = cast(CppObj)cast(void*)&_field;
109 
110       static if(withInitialize)
111         if(_field.value_ is null)
112             JSValueMember.ctor(ret);
113 
114         return ret;
115     }
116 
117 
118     inout(CppObj) cppObj() inout pure nothrow @trusted @property @nogc
119     {
120         if(_field.value_ is null)
121             return cast(inout(CppObj))cast(inout(void)*)&JSValueConsts._emptyInstance._field;
122         else
123             return cast(inout(CppObj))cast(inout(void)*)&_field;
124     }
125 
126 
127     bool getBooleanProperty(alias f)() const nothrow @nogc { return f(this.cppObj); }
128     alias isBoolean = getBooleanProperty!(JSValueMember.IsBoolean);
129     alias isInteger = getBooleanProperty!(JSValueMember.IsInteger);
130     alias isDouble = getBooleanProperty!(JSValueMember.IsDouble);
131     alias isNumber = getBooleanProperty!(JSValueMember.IsNumber);
132     alias isString = getBooleanProperty!(JSValueMember.IsString);
133     alias isArray = getBooleanProperty!(JSValueMember.IsArray);
134     alias isObject = getBooleanProperty!(JSValueMember.IsObject);
135     alias isNull = getBooleanProperty!(JSValueMember.IsNull);
136     alias isUndefined = getBooleanProperty!(JSValueMember.IsUndefined);
137 
138     bool has(T : bool)() const nothrow @nogc @property { return this.isBoolean; }
139     bool has(T : int)() const nothrow @nogc @property { return this.isInteger; }
140     bool has(T : double)() const nothrow @nogc @property { return this.isDouble; }
141     bool has(T : string)() const nothrow @nogc @property { return this.isString; }
142     bool has(T : WebString)() const nothrow @nogc @property { return this.isString; }
143     bool has(T : JSArray)() const nothrow @nogc @property { return this.isArray; }
144     bool has(T : JSObject)() const nothrow @nogc @property { return this.isObject; }
145     bool has(T : typeof(null))() const nothrow @nogc @property { return this.isNull; }
146 
147 
148     T get(T : bool)(T defVal = T.init) const nothrow @nogc
149     {
150         if(this.isBoolean)
151             return JSValueMember.ToBoolean(this.cppObj);
152         else
153             return defVal;
154     }
155 
156 
157     T get(T : int)(T defVal = T.init) const nothrow @nogc
158     {
159         if(this.isInteger)
160             return JSValueMember.ToInteger(this.cppObj);
161         else
162             return defVal;
163     }
164 
165 
166     T get(T : double)(T defVal = T.init) const nothrow @nogc
167     {
168         if(this.isDouble){
169             return to!double(to!string(this)); //JSValueMember.ToDouble(this.cppObj);
170         }else
171             return defVal;
172     }
173 
174 
175     T get(T : WebString)(T defVal = T.init) const nothrow @nogc
176     {
177         if(this.isString){
178             WebString str;
179             JSValueMember.ToString(this.cppObj, str.cppObj);
180             return str;
181         }else
182             return defVal;
183     }
184 
185 
186     T get(T)(T defVal = T.init) const nothrow @nogc
187     if(is(T == JSObject))
188     {
189         if(this.isObject)
190             return JSObject(JSValueMember.ToObject(this.cppObj));
191         else
192             return defVal;
193     }
194 
195 
196     WeakRef!JSObject get(T)(WeakRef!JSObject defVal = refP!JSObject(null))
197     if(is(T == WeakRef!JSObject))
198     {
199         if(this.isObject)
200             return .weakRef!JSObject(JSValueMember.ToObject(this.cppObj));
201         else
202             return defVal;
203     }
204 
205 
206     WeakRef!(const(JSObject)) get(T)(WeakRef!(const(JSObject)) defVal = refP!(const(JSObject))(null)) const
207     if(is(T == WeakRef!JSObject))
208     {
209         if(this.isObject)
210             return .weakRef!JSObject(JSValueMember.ToObject(this.cppObj));
211         else
212             return defVal;
213     }
214 
215 
216     T get(T)(T defVal = T.init) const nothrow @nogc
217     if(is(T == JSArray))
218     {
219         if(this.isArray)
220             return JSArray(JSValueMember.ToArray(this.cppObj));
221         else
222             return defVal;
223     }
224 
225 
226     WeakRef!(ApplySameTopQualifier!(This, JSArrayCpp))
227         get(T : WeakRef!JSArrayCpp, this This)
228         (WeakRef!(ApplySameTopQualifier!(This, JSArrayCpp)) defVal = WeakRef!(ApplySameTopQualifier!(This, JSArrayCpp)).init) inout nothrow @nogc
229     {
230       static if(is(This == const) || is(This == immutable))
231       {
232         if(this.isArray)
233             return .weakRef!JSArrayCpp(JSValueMember.ToArray(this.cppObj));
234         else
235             return defVal;
236       }
237       else
238       {
239         if(this.isArray)
240             return .weakRef!JSArrayCpp(JSValueMember.ToArray(cast(CppObj)this.cppObj));
241         else
242             return defVal;
243       }
244     }
245 
246 
247     void toString(scope void delegate(const(char)[]) sink) const
248     {
249         if(this.isArray)
250         {
251             auto ja = get!(WeakRef!JSArrayCpp);
252             ja.toString(sink);
253         }
254         else if(this.isObject)
255         {
256             auto jo = get!(WeakRef!JSObject);
257             jo.toString(sink);
258         }
259         else
260         {
261             WebString str;
262             JSValueMember.ToString(this.cppObj, str.cppObj);
263             foreach(char e; str.data.byChar)
264                 put(sink, e);
265         }
266     }
267 
268 
269     static
270     ref immutable(JSValue) undefined() pure nothrow @safe @nogc @property
271     {
272         return *JSValueConsts._undefined;
273     }
274 
275 
276     static
277     ref immutable(JSValue) null_() pure nothrow @safe @nogc @property
278     {
279         return *JSValueConsts._null;
280     }
281 
282 
283     static
284     auto weakRef(H)(H jsv)
285     if(is(H : const(awebview.wrapper.cpp.JSValue)))
286     {
287       static if(is(H == awebview.wrapper.cpp.JSValue))
288         JSValue* wsp = cast(JSValue*)cast(void*)jsv;
289       else static if(is(H == const(awebview.wrapper.cpp.JSValue)))
290         const(JSValue)* wsp = cast(const(JSValue)*)cast(const(void)*)jsv;
291       else
292         immutable(JSValue)* wsp = cast(immutable(JSValue)*)cast(immutable(void)*)jsv;
293 
294         return refP(wsp);
295     }
296 
297 
298   private:
299     alias CppObj = awebview.wrapper.cpp.JSValue;
300     CppObj.Field _field;
301 }
302 
303 unittest
304 {
305     JSValue v = true;
306     assert(v.isBoolean);
307     assert(v.get!bool);
308     
309     v = false;
310     assert(v.isBoolean);
311     assert(!v.get!bool);
312 }
313 
314 unittest
315 {
316     import std.conv;
317 
318     JSValue v = 12;
319     assert(v.isInteger);
320     assert(v.isNumber);
321     assert(!v.isDouble);
322     assert(!v.isObject);
323     assert(!v.isBoolean);
324     assert(v.get!int == 12);
325     assert(to!string(v) == "12");
326 
327     JSValue v2 = 12.5;
328     assert(!v2.isInteger);
329     assert(v2.isDouble);
330     assert(!v2.isBoolean);
331     import std.stdio;
332     assert(to!string(v2) == "12.5");
333 
334     JSValue v3 = "foo";
335     assert(v3.isString);
336     assert(to!string(v3) == "foo");
337 
338     JSArray ja = [1, 2, 3];
339     JSValue v4 = ja;
340     assert(v4.isArray);
341     auto v4arr = v4.get!(WeakRef!JSArrayCpp);
342     assert(v4arr.length == 3);
343 
344     const v5 = v4;
345     assert(v5.isArray);
346     auto v5arr = v5.get!(WeakRef!JSArrayCpp);
347     assert(v5arr.length == 3);
348 }