1 module awebview.wrapper.jsarray;
2 
3 import std.algorithm;
4 import std.format;
5 
6 import awebview.wrapper.cpp;
7 import awebview.wrapper.jsvalue : JSValue;
8 import awebview.wrapper.constants;
9 
10 import carbon.nonametype;
11 import carbon.templates;
12 import carbon.memory;
13 
14 
15 struct JSArray
16 {
17     alias PayloadType(This) = typeof(this.init.payload);
18 
19 
20     @property
21     ref JSArrayCpp payload() nothrow @nogc
22     {
23         if(!_instance.refCountedStore.isInitialized)
24             __ctor(cast(CppObj)null);
25 
26         return _instance.refCountedPayload;
27     }
28 
29 
30     @property
31     ref inout(JSArrayCpp) payload(this T)() inout nothrow @nogc
32     if(is(T == const) || is(T == immutable))
33     {
34         if(_instance.refCountedStore.isInitialized)
35             return *cast(typeof(return)*)&(this._instance.refCountedPayload());
36         else
37             return *cast(typeof(return)*)JSArrayConsts._emptyInstance;
38     }
39 
40 
41     this(size_t n) nothrow @nogc
42     in {
43         assert(n <= uint.max);
44     }
45     body {
46         _instance = Instance(RefCountedNoGC!JSArrayCpp(n));
47     }
48 
49 
50     this(in awebview.wrapper.cpp.JSArray cppInst) nothrow @nogc
51     {
52         _instance = Instance(RefCountedNoGC!JSArrayCpp(cppInst));
53     }
54 
55 
56     this(in awebview.wrapper.jsvalue.JSValue[] arr) nothrow @nogc
57     in {
58         assert(arr.length <= uint.max);
59     }
60     body {
61         _instance = Instance(RefCountedNoGC!JSArrayCpp(arr));
62     }
63 
64 
65     this(E)(in E[] arr) nothrow @nogc
66     if(is(typeof(awebview.wrapper.jsvalue.JSValue(arr[0]))))
67     in {
68         assert(arr.length <= uint.max);
69     }
70     body {
71         _instance = Instance(RefCountedNoGC!JSArrayCpp(arr));
72     }
73 
74 
75     JSArray dup() const nothrow @nogc
76     {
77         return JSArray(this.cppObj);
78     }
79 
80 
81     inout(CppObj) cppObj(this T)() inout nothrow @nogc @property
82     {
83         return cast(typeof(return))(cast(T*)&this).payload.cppObj;
84     }
85 
86 
87     uint length() const nothrow @nogc @property
88     { return payload.length; }
89 
90     alias opDollar = length;
91 
92     uint capacity() const nothrow @nogc @property
93     {
94         if(_instance.refCountedStore.refCount > 1)
95             return 0;
96         else
97             return payload.capacity;
98     }
99 
100     auto ref opIndex(size_t idx) nothrow @nogc
101     in{ assert(idx < this.length); }
102     body { return payload[cast(uint)idx]; }
103 
104     auto ref opIndex(size_t idx) const nothrow @nogc
105     in{ assert(idx < this.length); }
106     body { return payload[cast(uint)idx]; }
107 
108 
109     void pushBack()(auto ref const JSValue item) nothrow @nogc
110     {
111         if(!this._instance.refCountedStore.isInitialized)
112             this.payload.__ctor(cast(awebview.wrapper.cpp.JSArray)null);
113 
114         if(this._instance.refCountedStore.refCount > 1)
115             this = this.dup;
116 
117         this.payload.pushBack(item);
118     }
119 
120 
121     void opOpAssign(string op : "~")(auto ref const JSValue item) nothrow @nogc
122     {
123         pushBack(item);
124     }
125 
126 
127     void popBack() nothrow @nogc
128     {
129         if(!this._instance.refCountedStore.isInitialized)
130             this.payload.__ctor(cast(awebview.wrapper.cpp.JSArray)null);
131 
132         if(this._instance.refCountedStore.refCount > 1)
133             this = this.dup;
134 
135         this.payload.popBack();
136     }
137 
138 
139     void insert()(auto ref const JSValue item, size_t idx) nothrow @nogc
140     in { assert(idx < this.length); }
141     body {
142         if(!this._instance.refCountedStore.isInitialized)
143             this.payload.__ctor(cast(awebview.wrapper.cpp.JSArray)null);
144 
145         if(this._instance.refCountedStore.refCount > 1)
146             this = this.dup;
147 
148         this.payload.insert(item, idx);
149     }
150 
151 
152     void removeAt(size_t idx) nothrow @nogc
153     in { assert(idx < this.length); }
154     body {
155         if(!this._instance.refCountedStore.isInitialized)
156             this.payload.__ctor(cast(awebview.wrapper.cpp.JSArray)null);
157 
158         if(this._instance.refCountedStore.refCount > 1)
159             this = this.dup;
160 
161         this.payload.removeAt(cast(uint)idx);
162     }
163 
164 
165     void clear() nothrow @nogc
166     {
167         callAllDtor(this.payload);
168         assert(!this._instance.refCountedStore.isInitialized);
169     }
170 
171 
172     int opApply(int delegate(ref size_t, ref awebview.wrapper.jsvalue.JSValue) dg)
173     { return payload.opApply(dg); }
174 
175     int opApply(int delegate(ref size_t, ref const(awebview.wrapper.jsvalue.JSValue)) dg) const
176     { return payload.opApply(dg); }
177 
178     int opApply(int delegate(ref awebview.wrapper.jsvalue.JSValue) dg)
179     { return payload.opApply(dg); }
180 
181     int opApply(int delegate(ref const(awebview.wrapper.jsvalue.JSValue)) dg) const
182     { return payload.opApply(dg); }
183 
184 
185     void toString(scope void delegate(const(char)[]) sink) const
186     {
187         payload.toString(sink);
188     }
189 
190 
191   private:
192     Instance _instance;
193 
194     static auto _dummyTypeCreate()
195     {
196         static struct Dummy 
197         {
198             RefCountedNoGC!JSArrayCpp obj;
199             alias obj this;
200         }
201 
202         return Dummy();
203     }
204 
205     alias Instance = typeof(_dummyTypeCreate());
206     alias CppObj = awebview.wrapper.cpp.JSArray;
207 }
208 
209 
210 struct JSArrayCpp
211 {
212     this(size_t n) nothrow @nogc
213     in { assert(n <= uint.max); }
214     body {
215         JSArrayMember.ctor(this.cppObj!false, cast(uint)n);
216     }
217 
218 
219     this(in awebview.wrapper.cpp.JSArray cppInst) nothrow @nogc
220     {
221         if(cppInst)
222             JSArrayMember.ctor(this.cppObj!false, cppInst);
223         else
224             JSArrayMember.ctor(this.cppObj!false);
225     }
226 
227 
228     this(in awebview.wrapper.jsvalue.JSValue[] arr) nothrow @nogc
229     in { assert(arr.length <= uint.max); }
230     body {
231         this(arr.length);
232 
233         foreach(i, ref e; arr)
234             this[i] = e;
235     }
236 
237 
238     this(E)(in E[] arr) nothrow @nogc
239     if(is(typeof(awebview.wrapper.jsvalue.JSValue(arr[0]))))
240     in { assert(arr.length <= uint.max); }
241     body {
242         this(arr.length);
243 
244         foreach(i, const ref e; arr)
245             this[i] = awebview.wrapper.jsvalue.JSValue(e);
246     }
247 
248 
249     this(this) nothrow @nogc
250     {
251         if(this.isInitialized)
252         {
253             typeof(_field) f;
254             JSArrayCpp* p = cast(JSArrayCpp*)f.ptr;
255             JSArrayMember.ctor(p.cppObj!false, this.cppObj!false);
256             this._field = p._field;
257         }
258     }
259 
260 
261     ~this() nothrow @nogc
262     {
263         if(this.isInitialized)
264             JSArrayMember.dtor(this.cppObj!false);
265     }
266 
267 
268     private
269     ref inout(CppObj.Field) cppField() inout pure nothrow @trusted @property @nogc
270     {
271         return *cast(typeof(return)*)_field.ptr;
272     }
273 
274 
275     private
276     bool isInitialized() const pure nothrow @safe @nogc @property
277     { return cppField.vector_ !is null; }
278 
279 
280     CppObj cppObj(bool withInitialize = true)() nothrow @trusted @property @nogc
281     {
282         CppObj ret = cast(CppObj)cast(void*)&_field;
283 
284       static if(withInitialize)
285         if(!this.isInitialized)
286             JSArrayMember.ctor(ret);
287 
288         return ret;
289     }
290 
291 
292     inout(CppObj) cppObj() inout nothrow @trusted @property @nogc
293     {
294         if(!this.isInitialized)
295             return cast(inout(CppObj))cast(inout(void)*)&(JSArrayConsts._emptyInstance._field);
296         else
297             return cast(inout(CppObj))cast(inout(void)*)&_field;
298     }
299 
300 
301     uint length() const nothrow @nogc @property
302     {
303         return JSArrayMember.size(this.cppObj);
304     }
305 
306 
307     alias opDollar = length;
308 
309 
310     uint capacity() const nothrow @nogc @property
311     {
312         return JSArrayMember.capacity(this.cppObj);
313     }
314 
315 
316     ref awebview.wrapper.jsvalue.JSValue opIndex(size_t idx) nothrow @nogc
317     in{ assert(idx <= uint.max); }
318     body {
319         awebview.wrapper.cpp.JSValue obj = JSArrayMember.At(this.cppObj, cast(uint)idx);
320         return *cast(awebview.wrapper.jsvalue.JSValue*)cast(void*)obj;
321     }
322 
323 
324     ref const(awebview.wrapper.jsvalue.JSValue) opIndex(size_t idx) const nothrow @nogc
325     in { assert(idx <= uint.max); }
326     body {
327         const awebview.wrapper.cpp.JSValue obj = JSArrayMember.At(this.cppObj, cast(uint)idx);
328         return *cast(const(awebview.wrapper.jsvalue.JSValue)*)cast(const(void)*)obj;
329     }
330 
331 
332     void pushBack()(auto ref const awebview.wrapper.jsvalue.JSValue item) nothrow @nogc
333     {
334         JSArrayMember.Push(this.cppObj, item.cppObj);
335     }
336 
337 
338     void opOpAssign(string op : "~")(auto ref const awebview.wrapper.jsvalue.JSValue item) nothrow @nogc
339     {
340         pushBack(/*forward!*/item);
341     }
342 
343 
344     void popBack() nothrow @nogc
345     {
346         JSArrayMember.Pop(this.cppObj);
347     }
348 
349 
350     void insert()(auto ref const awebview.wrapper.jsvalue.JSValue item, uint idx) nothrow @nogc
351     {
352         JSArrayMember.Insert(this.cppObj, item.cppObj, idx);
353     }
354 
355 
356     void removeAt(uint idx) nothrow @nogc
357     {
358         JSArrayMember.Erase(this.cppObj, idx);
359     }
360 
361 
362     void clear() nothrow @nogc
363     {
364         JSArrayMember.Clear(this.cppObj);
365     }
366 
367 
368     int opApply(int delegate(ref size_t, ref awebview.wrapper.jsvalue.JSValue) dg)
369     {
370         int result;
371 
372         foreach(ref size_t i; 0 .. this.length){
373             result = dg(i, this[i]);
374             if(result)
375                 break;
376         }
377 
378         return result;
379     }
380 
381 
382     int opApply(int delegate(ref size_t, ref const(awebview.wrapper.jsvalue.JSValue)) dg) const
383     {
384         int result;
385 
386         foreach(ref size_t i; 0 .. this.length){
387             result = dg(i, this[i]);
388             if(result)
389                 break;
390         }
391 
392         return result;
393     }
394 
395 
396     int opApply(int delegate(ref awebview.wrapper.jsvalue.JSValue) dg)
397     {
398         int result;
399 
400         foreach(size_t i; 0 .. this.length){
401             result = dg(this[i]);
402             if(result)
403                 break;
404         }
405 
406         return result;
407     }
408 
409 
410     int opApply(int delegate(ref const(awebview.wrapper.jsvalue.JSValue)) dg) const
411     {
412         int result;
413 
414         foreach(size_t i; 0 .. this.length){
415             result = dg(this[i]);
416             if(result)
417                 break;
418         }
419 
420         return result;
421     }
422 
423 
424     @property
425     auto weakRef(this This)() inout nothrow @nogc
426     {
427         return JSArrayRange!This(cast(This*)&this, 0, this.length);
428     }
429 
430 
431     void toString(scope void delegate(const(char)[]) sink) const
432     {
433         sink("[");
434         foreach(i; 0 .. this.length){
435             formattedWrite(sink, "%s", this[i]);
436             if(i != this.length - 1)
437                 sink(", ");
438         }
439         sink("]");
440     }
441 
442 
443     static
444     auto weakRef(HandleJSArray)(HandleJSArray ws)
445     if(is(HandleJSArray : const(awebview.wrapper.cpp.JSArray)))
446     {
447       static if(is(HandleJSArray == awebview.wrapper.cpp.JSArray))
448         JSArrayCpp* wsp = cast(JSArrayCpp*)cast(void*)ws;
449       else static if(is(HandleJSArray == const(awebview.wrapper.cpp.JSArray)))
450         const(JSArrayCpp)* wsp = cast(const(JSArrayCpp)*)cast(const(void)*)ws;
451       else
452         immutable(JSArrayCpp)* wsp = cast(immutable(JSArrayCpp)*)cast(immutable(void)*)ws;
453 
454         return refP(wsp);
455     }
456 
457 
458   private:
459     alias CppObj = awebview.wrapper.cpp.JSArray;
460     ubyte[CppObj.Field.sizeof] _field;
461 }
462 
463 
464 unittest
465 {
466     auto arr = JSArrayCpp(8);
467     assert(arr.length == 8);
468 
469     foreach(ref awebview.wrapper.jsvalue.JSValue e; arr)
470         assert(e.isUndefined);
471 
472     arr[0] = awebview.wrapper.jsvalue.JSValue(12);
473     assert(arr[0].get!int == 12);
474 
475     arr ~= awebview.wrapper.jsvalue.JSValue(13);
476     assert(arr.length == 9);
477     assert(arr[$-1].get!int == 13);
478 
479     foreach(ref const awebview.wrapper.jsvalue.JSValue e; arr)
480         assert(e.isUndefined || e.isNumber);
481 }
482 
483 
484 unittest
485 {
486     import std.range;
487 
488     JSArrayCpp arr = ["foo", "bar", "hoge"];
489     auto arrRef = arr.weakRef;
490     static assert(isInputRange!(typeof(arrRef)));
491 }
492 
493 
494 private struct JSArrayRange(WRJSArray)
495 {
496   @nogc:
497   nothrow:
498     @property
499     auto ref front() inout nothrow @nogc { return (*_arr)[_b]; }
500 
501     void popFront() pure nothrow @nogc @safe { ++_b; }
502 
503     @property
504     auto ref back() inout nothrow @nogc { return (*_arr)[_e-1]; }
505 
506     void popBack() pure nothrow @nogc @safe { --_e; }
507 
508     @property
509     bool empty() const nothrow @nogc @safe { return _b >= _e; }
510 
511     @property
512     size_t length() const pure nothrow @nogc @safe { return _e - _b; }
513 
514     alias opDollar = length;
515 
516     auto ref opIndex(size_t i) inout nothrow @nogc
517     in{ assert(i < this.length); }
518     body{ return (*_arr)[_b + i]; }
519 
520     typeof(this) opSlice() pure nothrow @safe @nogc { return this; }
521 
522     typeof(this) opSlice(size_t a, size_t b) pure nothrow @safe @nogc
523     in{
524         assert(a <= b);
525         assert(b <= this.length);
526     }
527     body{
528         typeof(this) dst = this;
529         dst._b += a;
530         dst._e -= this.length - b;
531         return dst;
532     }
533 
534     WRJSArray* _arr;
535     ref inout(WRJSArray) getArray() inout pure nothrow @safe @nogc @property
536     { return *_arr; }
537 
538     alias getArray this;
539 
540   private:
541     size_t _b;
542     size_t _e;
543 }