1 module awebview.wrapper.webstring;
2 
3 import std.algorithm;
4 import std.traits;
5 import std.array;
6 
7 import awebview.wrapper.cpp;
8 import awebview.wrapper.weakref;
9 
10 import carbon.nonametype;
11 import carbon.memory;
12 import carbon.templates;
13 
14 
15 enum bool hasCppWebString(T) = is(typeof((T obj){
16     awebview.wrapper.cpp.WebString cpp = obj.cppObj;
17 }));
18 
19 
20 
21 /**
22 This type manages $(D WebStringCpp) by reference-counting.
23 */
24 struct WebString
25 {
26     alias PayloadType(This) = typeof(This.init.payload);
27 
28     /**
29     Return reference to $(D WebStringCpp) that is managed by reference-counting in $(D WebString).
30 
31     If $(D this) is not initialized yet,
32     $(D WebStringCpp) is initialized by $(awebview.wrapper.cpp.WebString.init).
33 
34     If $(D this) is $(D const) or $(D immutable) and $(D this) is not initialized yet,
35     this method returns a reference to global empty instance of immutable(WebStringCpp).
36     */
37     //@property
38     //ref WebStringCpp payload() nothrow @nogc
39     //{
40     //    if(!_instance.refCountedStore.isInitialized)
41     //        __ctor(cast(awebview.wrapper.cpp.WebString)null);
42 
43     //    return _instance.refCountedPayload;
44     //}
45 
46     ///// ditto
47     //@property
48     //ref inout(WebStringCpp) payload(this T)() inout nothrow @nogc
49     //if(is(T == const) || is(T == immutable))
50     //{
51     //    if(this._instance.refCountedStore.isInitialized)
52     //        return *cast(typeof(return)*)&(this._instance.refCountedPayload());
53     //    else
54     //        return *cast(typeof(return)*)WebStringCpp._emptyInstance;
55     //}
56     @property
57     auto ref payload(this T)() nothrow @nogc @trusted
58     {
59         static if(is(T == WebString))
60         {
61             if(!_instance.refCountedStore.isInitialized)
62                 __ctor(cast(awebview.wrapper.cpp.WebString)null);
63 
64             return _instance.refCountedPayload;
65         }
66         else static if(is(T == const) || is(T == immutable) || is(T == inout))
67         {
68             alias R = typeof(this._instance.refCountedPayload());
69 
70             if(this._instance.refCountedStore.isInitialized)
71                 return this._instance.refCountedPayload;
72             else
73                 return *cast(R*)WebStringCpp._emptyInstance;
74         }
75     }
76 
77     unittest {
78         WebString ws;
79         assert(!ws._instance.refCountedStore.isInitialized);
80 
81         // call this.payload
82         assert(ws.payload.cppField.instance_ !is null);
83         assert(ws._instance.refCountedStore.isInitialized);
84     }
85 
86     unittest {
87         const WebString cws;
88         assert(!cws._instance.refCountedStore.isInitialized);
89 
90         assert(&(cws.payload()) == WebStringCpp._emptyInstance);
91 
92         WebString ws = "foo";
93         const cws2 = ws;
94         assert(cws2._instance.refCountedStore.isInitialized);
95         assert(&(cws2.payload()) == &(ws.payload()));
96     }
97 
98 
99     /**
100     Construct $(D WebString) by copying.
101     */
102     this(ref WebStringCpp ws) nothrow @nogc
103     {
104         _instance = Instance(RefCountedNoGC!WebStringCpp(ws.cppObj));
105     }
106 
107 
108     /// ditto
109     this(in awebview.wrapper.cpp.WebString cppObj) nothrow @nogc
110     {
111         _instance = Instance(RefCountedNoGC!WebStringCpp(cppObj));
112     }
113 
114 
115     /// ditto
116     this(Char)(in Char[] str) nothrow @nogc
117     {
118         _instance = Instance(RefCountedNoGC!WebStringCpp(str));
119     }
120 
121     unittest {
122         WebStringCpp wcpp = "foobar";
123         WebString ws1 = wcpp;                   // ref WebStringCpp
124         assert(ws1.data == "foobar"w);
125         assert(ws1.cppObj !is wcpp.cppObj);     // constructed by copying.
126 
127         WebString ws2 = wcpp.cppObj;            // CppObj
128         assert(ws2.data == "foobar"w);
129         assert(ws2.cppObj !is wcpp.cppObj);     // constructed by copying.
130 
131         WebString ws3 = wcpp.data;              // const(wchar)[]
132         assert(ws3.data == "foobar"w);
133         assert(ws3.cppObj !is wcpp.cppObj);     // constructed by copying.
134     }
135 
136 
137     /**
138     Return a reference to an instance of the C++'s class $(D Awesomium::WebString).
139     */
140     @property
141     awebview.wrapper.cpp.WebString
142         cppObj() nothrow @nogc
143     { return payload.cppObj; }
144 
145 
146     /// ditto
147     @property
148     inout(awebview.wrapper.cpp.WebString)
149         cppObj(this T)() inout pure nothrow @nogc
150     if(is(T == const) || is(T == immutable))
151     { return (cast(T*)&this).payload.cppObj; }
152 
153     unittest {
154         WebString ws = "foobar";
155         assert(ws.cppObj is ws.payload.cppObj);
156 
157         const cws = ws;
158         assert(ws.cppObj is cws.cppObj);
159         assert(cws.cppObj is cws.payload.cppObj);
160 
161         WebString e;
162         const WebString ce = e;
163         assert(ce.cppObj is WebStringCpp._emptyInstance.cppObj);
164     }
165 
166 
167     /**
168     Return reference to $(D WebStringCpp) that is managed by reference-counting in $(D WebString).
169     */
170     @property
171     WeakRef!(PayloadType!This) weakRef(this This)() inout nothrow @nogc
172     {
173         alias WSCpp = PayloadType!This;
174         return refP!WSCpp(cast(WSCpp*)&(cast(This*)&this).payload());
175     }
176 
177     unittest {
178         
179     }
180 
181 
182     @property
183     WebString dup() const @nogc
184     {
185         auto cppObj = this.payload.cppObj;
186         return WebString(cppObj);
187     }
188 
189 
190     void opAssign(WebString ws) @nogc
191     { _instance = ws._instance; }
192 
193     void opAssign(Char)(const(Char)[] str) @nogc
194     { this = WebString(str); }
195 
196     void opAssign(ref const WebStringCpp wrefstr) @nogc
197     { this = WebString(wrefstr.cppObj); }
198 
199     void opAssign(in awebview.wrapper.cpp.WebString cppObj) @nogc
200     { this = WebString(cppObj); }
201 
202 
203 
204     const(wchar)* ptr() const nothrow @property @nogc
205     { return payload.ptr; }
206 
207 
208     alias opDollar = length;
209 
210     size_t length() const nothrow @property @nogc
211     { return payload.length; }
212 
213 
214     bool empty() const nothrow @property @nogc
215     { return payload.empty; }
216 
217 
218     const(wchar)[] data() const nothrow @property @nogc
219     { return this.ptr[0 .. this.length]; }
220 
221 
222     const(wchar)[] opSlice() const nothrow @property @nogc
223     { return this.data; }
224 
225 
226     void opOpAssign(string op : "~")(const WebString ws) nothrow @nogc
227     {
228         this ~= ws.cppObj;
229     }
230 
231 
232     void opOpAssign(string op : "~")(ref const WebStringCpp ws) nothrow @nogc
233     {
234         this ~= ws.cppObj;
235     }
236 
237 
238     void opOpAssign(string op : "~", Char)(const(Char)[] str) nothrow @nogc
239     {
240         if(!this._instance.refCountedStore.isInitialized){
241             __ctor(ws.cppObj);
242             return;
243         }
244 
245         if(this._instance.refCountedStore.refCount > 1)
246             this = this.dup;
247 
248         this.payload ~= str;
249     }
250 
251 
252     void opOpAssign(string op : "~")(in awebview.wrapper.cpp.WebString cppObj)
253     {
254         if(!this._instance.refCountedStore.isInitialized){
255             __ctor(ws.cppObj);
256             return;
257         }
258 
259         if(this._instance.refCountedStore.refCount > 1)
260             this = this.dup;
261 
262         this.payload ~= cppObj;
263     }
264 
265 
266     void clear()
267     {
268         _instance.__xdtor();
269         assert(!_instance.refCountedStore.isInitialized);
270     }
271 
272 
273     bool opEquals(const WebString rhs) const nothrow @nogc
274     { return this.payload.opEquals(rhs.payload); }
275 
276     bool opEquals(ref const WebString rhs) const nothrow @nogc
277     { return this.payload.opEquals(rhs.payload); }
278 
279     bool opEquals(ref const WebStringCpp rhs) const nothrow @nogc
280     { return this.payload.opEquals(rhs); }
281 
282     bool opEquals(Char)(const Char[] rhs) const
283     { return this.payload.opEquals(rhs); }
284 
285     int opCmp(const WebString rhs) const nothrow @nogc
286     { return this.payload.opCmp(rhs.payload); }
287 
288     int opCmp(ref const WebString rhs) const nothrow @nogc
289     { return this.payload.opCmp(rhs.payload); }
290 
291     int opCmp(ref const WebStringCpp rhs) const nothrow @nogc
292     { return this.payload.opCmp(rhs); }
293 
294     int opCmp(Char)(const Char[] rhs) const
295     if(isSomeChar!Char)
296     { return this.payload.opCmp(rhs); }
297 
298 
299     const(wchar)[] toString() const nothrow @nogc
300     { return this.data; }
301 
302   private:
303     /// RefCountedNoGC!WebStringCpp _instance;
304     Instance _instance;
305 
306     alias Instance = typeof(_dummyTypeCreate());
307 
308     static auto _dummyTypeCreate()
309     {
310         static struct R
311         {
312             RefCountedNoGC!WebStringCpp obj;
313             alias obj this;
314         }
315 
316         return R();
317     }
318 }
319 
320 
321 
322 struct WebStringCpp
323 {
324     alias data this;    // const(wchar)[] data() const @property;
325 
326     this(const(char)[] str) nothrow @nogc
327     in{
328         assert(str.length <= uint.max);
329     }
330     body{
331         WebStringMember.ctor(this.cppObj!false);
332         WebStringMember.CreateFromUTF8(str.ptr, cast(uint)str.length, this.cppObj!false);
333     }
334 
335     unittest
336     {
337         WebStringCpp ws = "foobar";
338         assert(ws.data == "foobar");
339     }
340 
341 
342     this(const(wchar)[] str) nothrow @nogc
343     in{
344         assert(str.length <= uint.max);
345     }
346     body{
347         WebStringMember.ctor(this.cppObj!false, cast(const(ushort)*)str.ptr, cast(uint)str.length);
348     }
349 
350 
351     this(const awebview.wrapper.cpp.WebString ws) nothrow @nogc
352     {
353         if(ws !is null)
354             WebStringMember.ctor(this.cppObj!false, ws);
355         else
356             WebStringMember.ctor(this.cppObj!false);
357     }
358 
359 
360     this(this) nothrow @nogc
361     {
362         if(this.cppField.instance_ !is null){
363             typeof(_field) f;
364             WebStringCpp* vp = cast(WebStringCpp*)cast(void*)&f;
365             WebStringMember.ctor(vp.cppObj!false, this.cppObj);
366             this._field = vp._field;
367         }
368     }
369 
370 
371     ~this() nothrow @nogc
372     {
373         if(this.cppField.instance_ !is null)
374             WebStringMember.dtor(this.cppObj!false);
375     }
376 
377 
378     private
379     ref inout(awebview.wrapper.cpp.WebString.Field)
380         cppField() inout pure nothrow @trusted @property @nogc
381     { return *cast(typeof(return)*)_field.ptr; }
382 
383 
384     awebview.wrapper.cpp.WebString
385         cppObj(bool withInitialize = true)() nothrow @trusted @property @nogc
386     {
387         typeof(return) o = cast(typeof(return))cast(void*)_field.ptr;
388 
389       static if(withInitialize)
390         if(cppField.instance_ is null)
391             WebStringMember.ctor(o);
392 
393         return o;
394     }
395 
396 
397     inout(awebview.wrapper.cpp.WebString)
398         cppObj() inout pure nothrow @trusted @property @nogc
399     {
400         if(cppField.instance_ is null)
401             return cast(typeof(return))cast(inout(void)*)(_emptyInstance._field.ptr);
402         else
403             return cast(typeof(return))cast(inout(void)*)_field.ptr;
404     }
405 
406 
407     WeakRef!WebStringCpp weakRef(this T)() inout pure nothrow @trusted @property @nogc
408     {
409         return refP!T(cast(T*)&this);
410     }
411 
412 
413     void opAssign()(auto ref const WebStringCpp str) nothrow @nogc
414     {
415         WebStringMember.opAssign(this.cppObj, str.cppObj);
416     }
417 
418 
419     void opAssign(Char)(const(Char)[] c) nothrow @nogc
420     if(is(Char ==  char) || is(Char == wchar))
421     {
422         this.clear();
423         this ~= c;
424     }
425 
426 
427     const(wchar)* ptr() const nothrow @property @nogc
428     {
429         return cast(const(wchar)*)WebStringMember.data(this.cppObj);
430     }
431 
432 
433     alias opDollar = length;
434 
435     size_t length() const nothrow @property @nogc
436     {
437         return WebStringMember.length(this.cppObj);
438     }
439 
440 
441     bool empty() const nothrow @property @nogc
442     {
443         return WebStringMember.IsEmpty(this.cppObj);
444     }
445 
446 
447     const(wchar)[] data() const nothrow @property @nogc
448     {
449         return this.ptr[0 .. this.length];
450     }
451 
452 
453     const(wchar)[] opSlice() const nothrow @property @nogc
454     {
455         return this.data;
456     }
457 
458 
459     const(wchar)[] opSlice(size_t a, size_t b) const nothrow @property @nogc
460     {
461         return this.data[a .. b];
462     }
463 
464 
465     void opOpAssign(string op : "~")(auto ref const WebStringCpp s) nothrow @nogc
466     {
467         WebStringMember.Append(this.cppObj, s.cppObj);
468     }
469 
470 
471     void opOpAssign(string op : "~", Char)(const(Char)[] str) nothrow @nogc
472     if(is(Char == char) || is(Char == wchar))
473     {
474         auto ws = WebStringCpp(str);
475         this.opOpAssign!"~"(ws);
476     }
477 
478 
479     void opOpAssign(string op : "~")(awebview.wrapper.cpp.WebString cppObj) nothrow @nogc
480     {
481         WebStringMember.Append(this.cppObj, cppObj);
482     }
483 
484 
485     void clear() nothrow @nogc
486     {
487         WebStringMember.Clear(this.cppObj);
488     }
489 
490 
491     bool opEquals()(auto ref const WebStringCpp rhs) const nothrow @nogc
492     {
493         return WebStringMember.opEquals(this.cppObj, rhs.cppObj);
494     }
495 
496 
497     bool opEquals(Char)(const Char[] rhs) const
498     if(isSomeChar!Char)
499     {
500         static if(is(Char == wchar))
501             return this.data == rhs;
502         else
503             return opCmp(rhs) == 0;
504     }
505 
506 
507     int opCmp()(auto ref const WebStringCpp rhs) const
508     {
509         return WebStringMember.opCmp(this.cppObj, rhs.cppObj);
510     }
511 
512 
513     int opCmp(Char)(const Char[] rhs) const
514     if(isSomeChar!Char)
515     {
516         return cmp(this.data, rhs);
517     }
518 
519 
520     const(wchar)[] toString() const nothrow @nogc
521     {
522         return this.data;
523     }
524 
525 
526     static
527     auto weakRef(HandleWS)(HandleWS ws) @trusted
528     if(is(HandleWS : const(awebview.wrapper.cpp.WebString)))
529     {
530       static if(is(HandleWS == awebview.wrapper.cpp.WebString))
531         WebStringCpp* wsp = cast(WebStringCpp*)cast(void*)ws;
532       else static if(is(HandleWS == const(awebview.wrapper.cpp.WebString)))
533         const(WebStringCpp)* wsp = cast(const(WebStringCpp)*)cast(const(void)*)ws;
534       else
535         immutable(WebStringCpp)* wsp = cast(immutable(WebStringCpp)*)cast(immutable(void)*)ws;
536 
537         return refP(wsp);
538     }
539 
540 
541   private:
542     ubyte[awebview.wrapper.cpp.WebString.Field.sizeof] _field;
543 
544     static shared immutable(WebStringCpp*) _emptyInstance;
545 
546     shared static this()
547     {
548         WebStringCpp* s = new WebStringCpp;
549         WebStringMember.ctor(s.cppObj!false);
550 
551         _emptyInstance = cast(immutable)s;  // s is unique
552     }
553 }
554 
555 
556 unittest {
557     WebStringCpp empty_;
558     assert(empty_.length == 0);
559     assert(empty_.empty);
560     //assert(empty_.ptr is null);
561     assert(empty_ == empty_);
562     assert(empty_ == "");
563     assert(empty_ == ""w);
564     assert(empty_ == ""d);
565     empty_ ~= "foobar";
566     assert(empty_ == "foobar");
567     empty_ ~= "ああああ";
568     assert(empty_ == "foobarああああ");
569 }
570 
571 unittest {
572     import std.conv : to;
573 
574     wstring src = "オーサミウムオーサミウムおーさみうむ";
575     WebStringCpp str = src;
576     assert(str.length == src.length);
577     assert(!str.empty);
578     assert(str.ptr != src.ptr); // copy
579     assert(str == src);
580     assert(str.data == src);
581     assert(str == to!string(src));
582     assert(str[0 .. 3] == src[0 .. 3]);
583     assert(str[0 .. $] == src[0 .. $]);
584 
585     WebStringCpp str2 = "foobar"w;
586     assert(str2.length == 6);
587     assert(!str2.empty);
588     assert(str2[0 .. 3] == "foo");
589     assert(str2[3 .. $] == "bar");
590     assert(str2.front == 'f');
591 
592     str2 = str;
593     assert(str2 == str);
594 
595     WebStringCpp str3 = str;
596     assert(str3.ptr != str.ptr);
597     assert(str3 == str);
598 }
599 
600 unittest {
601     WebStringCpp ws;
602     auto r1 = ws.weakRef;
603     auto r2 = ws.cppObj.weakRef!WebStringCpp;
604 
605     assert(ws.cppObj is r1.cppObj);
606     assert(r1.cppObj is r2.cppObj);
607 
608     ws = "foobar";
609     assert(ws.cppObj is r1.cppObj);
610     assert(ws.cppObj is r2.cppObj);
611 
612     r1 ~= "foobar";
613     assert(ws == "foobarfoobar");
614 
615     ws = WebStringCpp("f");
616     assert(ws.cppObj is r1.cppObj);
617     assert(ws.cppObj is r2.cppObj);
618 
619     ws.clear();
620     assert(ws.cppObj is r1.cppObj);
621     assert(ws.cppObj is r2.cppObj);
622 }