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