1 /**
2 The most important data type in Godot.
3 
4 Copyright:
5 Copyright (c) 2007 Juan Linietsky, Ariel Manzur.
6 Copyright (c) 2014 Godot Engine contributors (cf. AUTHORS.md)
7 Copyright (c) 2017 Godot-D contributors
8 Copyright (c) 2022 Godot-DLang contributors
9 
10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
11 
12 
13 */
14 module godot.variant;
15 
16 import godot.abi;
17 import godot;
18 import godot.object;
19 import godot.api.traits;
20 import godot.api.reference;
21 import godot.script;
22 import godot.api.types;
23 
24 import std.meta, std.traits;
25 import std.conv : text;
26 import std.range;
27 
28 // for tests
29 import godot.node;
30 import godot.resource;
31 
32 // FIXME ABI type should probably have its own `version`...
33 version (X86_64) {
34     version (DigitalMars) {
35         version (linux) version = GodotSystemV;
36         version (OSX) version = GodotSystemV;
37         version (Posix) version = GodotSystemV;
38     }
39 }
40 
41 /// User-defined Variant conversions.
42 /// For structs and classes, constructors and member functions can also be used.
43 unittest {
44     struct A {
45     }
46 
47     static assert(!Variant.compatible!A);
48 
49     struct B {
50     }
51 
52     B to(T : B)(Variant v) {
53         return B();
54     }
55 
56     int to(T : Variant)(B b) {
57         return 1;
58     }
59 
60     static assert(Variant.compatible!B);
61 
62     struct C {
63         this(Variant v) {
64         }
65 
66         Variant to(T : Variant)() {
67             return Variant(1);
68         }
69     }
70 
71     static assert(Variant.compatible!C);
72 
73     B b;
74     C c;
75 
76     Variant vb = b;
77     Variant vc = c;
78 
79     b = vb.as!B;
80     c = vc.as!C;
81 }
82 
83 /// 
84 enum VariantType {
85     nil,
86 
87     // atomic types
88     bool_,
89     int_,
90     float_,
91     string,
92 
93     // math types
94 
95     vector2, // 5
96     vector2i,
97     rect2,
98     rect2i,
99     vector3,
100     vector3i, // 10
101     transform2d,
102     vector4,
103     vector4i,
104     plane,
105     quaternion,
106     aabb,
107     basis, // 15
108     transform3d,
109     projection,
110 
111     // misc types
112     color,
113     string_name,
114     node_path,
115     rid,
116     object, // 20
117     callable,
118     signal,
119     dictionary,
120     array,
121 
122     // arrays
123     packed_byte_array, // 25
124     packed_int32_array,
125     packed_int64_array,
126     packed_float32_array,
127     packed_float64_array,
128     packed_string_array, // 30
129     packed_vector2_array,
130     packed_vector3_array,
131     packed_color_array,
132 }
133 
134 /**
135 Godot's tagged union type.
136 
137 Primitives, Godot core types, and `GodotObject`-derived classes can be stored in
138 a Variant. Other user-defined D types can be made compatible with Variant by
139 defining `to!CustomType(Variant)` and `to!Variant(CustomType)` functions.
140 
141 Properties and method arguments/returns are passed between Godot and D through
142 Variant, so these must use Variant-compatible types.
143 */
144 struct Variant {
145     package(godot) godot_variant _godot_variant;
146 
147     // having it inside variant creates annoying recursive alias issue
148     alias Type = VariantType;
149 
150     /// GDExtension type that gets passed to the C functions
151     // NOTE: godot 4 now uses default int as int32 and double precision by default
152     // TODO: verify this
153     alias InternalType = AliasSeq!(
154         typeof(null),
155 
156         godot_bool,
157         int,
158         double,
159         godot_string,
160 
161         godot_vector2, // 5
162         godot_vector2i,
163         godot_rect2,
164         godot_rect2i,
165         godot_vector3,
166         godot_vector3i, // 10
167         godot_transform2d,
168         godot_vector4,
169         godot_vector4i,
170         godot_plane,
171         godot_quaternion,
172         godot_aabb,
173         godot_basis, // 15
174         godot_transform3d,
175         godot_projection,
176 
177         godot_color,
178         godot_string, //godot_string_name
179         godot_node_path,
180         godot_rid, // 20
181         godot_object,
182         godot_callable,
183         godot_signal,
184         godot_dictionary,
185         godot_array, // 25
186 
187         godot_packed_byte_array,
188         godot_packed_int32_array,
189         godot_packed_int64_array,
190         godot_packed_float32_array,
191         godot_packed_float64_array, // 30
192         godot_packed_string_array,
193         godot_packed_vector2_array,
194         godot_packed_vector3_array,
195         godot_packed_color_array,
196     );
197 
198     /// D type that this Variant implementation uses
199     alias DType = AliasSeq!(
200         typeof(null),
201 
202         bool,
203         long,
204         double,
205         String,
206 
207         Vector2, // 5
208         Vector2i,
209         Rect2,
210         Rect2i,
211         Vector3,
212         Vector3i, // 10
213         Transform2D,
214         Vector4,
215         Vector4i,
216         Plane,
217         Quaternion,
218         AABB,
219         Basis, // 15
220         Transform3D,
221         Projection,// misc types
222         Color,
223         StringName,
224         NodePath,
225         RID, // 20
226         GodotObject,
227         GodotCallable,
228         GodotSignal,
229         Dictionary,
230         Array, // 25
231 
232         // arrays
233         PackedByteArray,
234         PackedInt32Array,
235         PackedInt64Array,
236         PackedFloat32Array,
237         PackedFloat64Array, // 30
238         PackedStringArray,
239         PackedVector2Array,
240         PackedVector3Array,
241         PackedColorArray,
242     );
243 
244     /// 
245     enum Operator {
246         //comparation
247         equal,
248         notEqual,
249         less,
250         lessEqual,
251         greater,
252         greaterEqual,
253 
254         //mathematic
255         add,
256         substract,
257         multiply,
258         divide,
259         negate,
260         positive,
261         modulus,
262         power,
263         //stringConcat,
264 
265         //bitwise
266         shiftLeft,
267         shiftRight,
268         bitAnd,
269         bitOr,
270         bitXor,
271         bitNegate,
272 
273         //logic
274         and,
275         or,
276         xor,
277         not,
278 
279         //containment
280         in_
281     }
282 
283     private enum bool implicit(Src, Dest) = is(Src : Dest) || isImplicitlyConvertible!(Src, Dest);
284 
285     private static GodotObject objectToGodot(T)(T o) {
286         return o.getGodotObject;
287     }
288 
289     // Conversions for non-core types provided by Variant.
290     // Lower priority than user-defined `to` functions, to allow overriding
291     // default blanket implementations.
292     private alias internalAs(T) = (Variant v) => v.as!T;
293     private enum bool hasInternalAs(T) = __traits(compiles, internalAs!T);
294     private alias internalFrom(T) = (T t) => Variant.from(t);
295     private enum bool hasInternalFrom(T) = __traits(compiles, internalFrom!T);
296 
297     ///
298     T as(T)() const if (isStaticArray!T && compatibleFromGodot!(ElementType!T)) {
299         return as!Array.as!T;
300     }
301 
302     /// 
303     T as(T)() const if (is(T : TypedArray!U, U...) && !is(U == Array)) {
304         return T(as!Array);
305     }
306 
307     T as(T : void*)() {
308         return cast(T)&_godot_variant;
309     }
310 
311     /// 
312     T as(T)() const 
313             if ((isGodotClass!T && !is(T == GodotObject)) || is(T : Ref!U, U)) {
314         GodotObject o = cast()(as!GodotObject);
315         return o.as!T;
316     }
317 
318     ///
319     static Array from(T)(T t)
320             if ((isForwardRange!T || isStaticArray!T) && compatibleToGodot!(ElementType!T)) {
321         return Array.from(t);
322     }
323 
324     ///
325     GodotType as(T : GodotType)() const {
326         if (type == Type.object) {
327             Ref!Script s = as!Script;
328             if (s)
329                 return GodotType(s);
330             else
331                 return GodotType.init;
332         } else if (type == Type..string)
333             return GodotType(BuiltInClass(as!String));
334         else if (type == Type.int_)
335             return GodotType(cast(Variant.Type)(as!int));
336         else
337             return GodotType.init;
338     }
339 
340     ///
341     static Variant from(T : GodotType)(T t) {
342         import std.sumtype : match;
343 
344         Variant ret;
345         t.match!(
346             (Variant.Type t) { ret = cast(int) t; },
347             (BuiltInClass c) { ret = c.name; },
348             (Ref!Script s) { ret = s; }
349         );
350         return ret;
351     }
352 
353     // TODO: fix me
354     /*
355 	static assert(hasInternalAs!Node, internalAs!Node);
356 	static assert(hasInternalAs!(Ref!Resource), internalAs!(Ref!Resource));
357 	static assert(!hasInternalAs!Object); // `directlyCompatible` types not handled by internalAs
358 	static assert(hasInternalAs!(int[4]), internalAs!(int[4]));
359 	static assert(hasInternalFrom!(int[4]), internalFrom!(int[4]));
360 	static assert(!hasInternalAs!(int[]));
361 	static assert(hasInternalFrom!(int[]), internalFrom!(int[]));
362 	static assert(hasInternalAs!GodotType, internalAs!GodotType);
363 	static assert(hasInternalFrom!GodotType, internalFrom!GodotType);
364 	static assert(compatible!GodotType);
365 	*/
366 
367     private template getToVariantFunction(T) {
368         mixin("import " ~ moduleName!T ~ ";");
369         alias getToVariantFunction = (T t) { Variant v = t.to!Variant; return v; };
370     }
371 
372     enum bool hasToVariantFunction(T) = __traits(compiles, getToVariantFunction!T);
373 
374     private template getVariantConstructor(T) {
375         alias getVariantConstructor = (Variant v) => T(v);
376     }
377 
378     enum bool hasVariantConstructor(T) = __traits(compiles, getVariantConstructor!T);
379 
380     template getFromVariantFunction(T) {
381         mixin("import " ~ moduleName!T ~ ";");
382         alias getFromVariantFunction = (Variant v) { T ret = v.to!T; return ret; };
383     }
384 
385     enum bool hasFromVariantFunction(T) = __traits(compiles, getFromVariantFunction!T);
386 
387     /// function to convert T to an equivalent Godot type
388     template conversionToGodot(T) {
389         static if (isGodotClass!T)
390             alias conversionToGodot = objectToGodot!T;
391         else static if (is(T : GodotStringLiteral!s, string s))
392             alias conversionToGodot = (T t) => t.str();
393         else static if (is(T : Ref!U, U))
394             alias conversionToGodot = objectToGodot!U;
395         else static if (isIntegral!T)
396             alias conversionToGodot = (T t) => cast(long) t;
397         else static if (isFloatingPoint!T)
398             alias conversionToGodot = (T t) => cast(double) t;
399         else static if (implicit!(T, const(char)[]) || implicit!(T, const(char)*))
400             alias conversionToGodot = (T t) => String(t);
401         else static if (hasToVariantFunction!T) {
402             alias conversionToGodot = getToVariantFunction!T;
403         } else static if (hasInternalFrom!T)
404             alias conversionToGodot = internalFrom!T;
405         else
406             alias conversionToGodot = void; // none
407     }
408 
409     enum bool convertsToGodot(T) = isCallable!(conversionToGodot!T);
410     alias conversionToGodotType(T) = Unqual!(ReturnType!(conversionToGodot!T));
411 
412     /// function to convert a Godot-compatible type to T
413     template conversionFromGodot(T) {
414         static if (isIntegral!T)
415             alias conversionFromGodot = (long v) => cast(T) v;
416         else static if (isFloatingPoint!T)
417             alias conversionFromGodot = (double v) => cast(T) v;
418         else static if (hasVariantConstructor!T) {
419             alias conversionFromGodot = getVariantConstructor!T;
420         } else static if (hasFromVariantFunction!T) {
421             alias conversionFromGodot = getFromVariantFunction!T;
422         } else
423             alias conversionFromGodot = void;
424     }
425 
426     enum bool convertsFromGodot(T) = isCallable!(conversionFromGodot!T);
427     alias conversionFromGodotType(T) = Unqual!(Parameters!(conversionFromGodot!T)[0]);
428 
429     enum bool directlyCompatible(T) = staticIndexOf!(Unqual!T, DType) != -1;
430     template compatibleToGodot(T) {
431         static if (directlyCompatible!T)
432             enum bool compatibleToGodot = true;
433         else
434             enum bool compatibleToGodot = convertsToGodot!T;
435     }
436 
437     template compatibleFromGodot(T) {
438         static if (directlyCompatible!T)
439             enum bool compatibleFromGodot = true;
440         else static if (hasInternalAs!T)
441             enum bool compatibleFromGodot = true;
442         else
443             enum bool compatibleFromGodot = convertsFromGodot!T;
444     }
445 
446     enum bool compatible(R) = compatibleToGodot!(R) && compatibleFromGodot!(R);
447 
448     /// All target Variant.Types that T could implicitly convert to, as indices
449     private template implicitTargetIndices(T) {
450         private enum bool _implicit(size_t di) = implicit!(T, DType[di]);
451         alias implicitTargetIndices = Filter!(_implicit, aliasSeqOf!(iota(DType.length)));
452     }
453 
454     /++
455 	Get the Variant.Type of a compatible D type. Incompatible types return nil.
456 	+/
457     public template variantTypeOf(T) {
458         import std.traits, godot;
459 
460         static if (directlyCompatible!T) {
461             enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(Unqual!T, DType)];
462         } else static if (convertsToGodot!T) {
463             static if (is(conversionToGodotType!T : Variant))
464                 enum Type variantTypeOf = Type.nil;
465             else
466                 enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(
467                             conversionToGodotType!T, DType)];
468         } else
469             enum Type variantTypeOf = Type.nil; // so the template always returns a Type
470     }
471 
472     /// 
473     R as(R)() const 
474             if (!is(R == Variant) && !is(R == typeof(null)) && (convertsFromGodot!R || directlyCompatible!R)) {
475         static if (directlyCompatible!R)
476             enum VarType = variantTypeOf!R;
477         else static if (is(conversionFromGodotType!R : Variant))
478             enum VarType = Type.nil;
479         else
480             enum VarType = EnumMembers!Type[staticIndexOf!(conversionFromGodotType!R, DType)];
481 
482         static if (VarType == Type.nil) {
483             return conversionFromGodot!R(this);
484         } else static if (is(Unqual!R == String)) {
485             static if (is(Unqual!R == NodePath))
486                 godot_node_path str;
487             else
488                 godot_string str;
489             _godot_api.variant_stringify(&_godot_variant, cast(void*)&str);
490             return R(str);
491         } else {
492             DType[VarType] ret = void;
493             //*cast(InternalType[VarType]*)&ret = mixin("_godot_api.variant_as_"~FunctionAs!VarType~"(&_godot_variant)");
494 
495             // this gives wrong result, try the other way around
496             //auto fn = _godot_api.get_variant_from_type_constructor(VarType);
497             //fn(cast(GDExtensionVariantPtr) &_godot_variant, &ret);
498 
499             // special case such as calling function with null optional parameter
500             if (_godot_variant._opaque.ptr is null) {
501                 return R.init;
502             }
503             import core.stdc.string;
504             memset(&ret, 0, ret.sizeof);
505             
506             auto fn = _godot_api.get_variant_to_type_constructor(cast(GDExtensionVariantType) VarType);
507             fn(cast(void*)&ret, cast(void*)&_godot_variant);
508 
509             static if (directlyCompatible!R)
510                 return ret;
511             else {
512                 return conversionFromGodot!R(ret);
513             }
514         }
515     }
516 
517     this(R)(auto ref R input) if (!is(R : Variant) && !is(R : typeof(null))) {
518         static assert(compatibleToGodot!R, R.stringof ~ " isn't compatible with Variant.");
519         enum VarType = variantTypeOf!R;
520 
521         static if (VarType == Type.nil) {
522             this = conversionToGodot!R(input);
523         } else {
524             //mixin("auto Fn = _godot_api.variant_new_"~FunctionNew!VarType~";");
525             // auto Fn = _godot_api.get_variant_from_type_constructor(VarType);
526             auto Fn = _godot_api.get_variant_from_type_constructor(cast(GDExtensionVariantType) VarType);
527             alias PassType = Parameters!Fn[1]; // second param is the value
528 
529             alias IT = InternalType[VarType];
530 
531             // handle explicit conversions
532             static if (directlyCompatible!R)
533                 alias inputConv = input;
534             else
535                 auto inputConv = conversionToGodot!R(input);
536 
537             static if (is(IT == Unqual!PassType))
538                 Fn(&_godot_variant, cast(IT) inputConv); // value
539             else
540                 Fn(&_godot_variant, cast(IT*)&inputConv); // pointer
541         }
542     }
543 
544     pragma(inline, true)
545     void opAssign(T)(in auto ref T input)
546             if (!is(T : Variant) && !is(T : typeof(null)) && !is(Unqual!T : void*)) {
547         import std.conv : emplace;
548 
549         _godot_api.variant_destroy(&_godot_variant);
550         static if (is(T : TypedArray!Args, Args...)) {
551             // hacky way, for some reasons 'alias this' was ignored
552             emplace!(Variant)(&this, input._array);
553         } else {
554             emplace!(Variant)(&this, input);
555         }
556     }
557 
558     // internal use only, but can be useful for users who knows what they are doing
559     // used in few cases only, audioeffect.process() as a buffer is one example
560     pragma(inline, true)
561     package(godot) void opAssign(const void* input) {
562         // can it be messed up by alignment?
563         _godot_variant = *cast(godot_variant*) input;
564     }
565 
566     static assert(allSatisfy!(compatible, DType));
567     static assert(!compatible!Object); // D Object
568 
569     static assert(directlyCompatible!GodotObject);
570     static assert(directlyCompatible!(const(GodotObject)));
571     static assert(!directlyCompatible!Node);
572     // TODO: fix me
573     //static assert(compatibleFromGodot!Node);
574     static assert(compatibleToGodot!Node);
575     // TODO: fix me
576     //static assert(compatibleFromGodot!(const(Node)));
577     static assert(compatibleToGodot!(const(Node)));
578     static assert(!directlyCompatible!(Ref!Resource));
579     // TODO: fix me
580     //static assert(compatibleFromGodot!(Ref!Resource));
581     static assert(compatibleToGodot!(Ref!Resource));
582     // TODO: fix me
583     //static assert(compatibleFromGodot!(const(Ref!Resource)));
584     static assert(compatibleToGodot!(const(Ref!Resource)));
585 
586     private template FunctionAs(Type type) {
587         private enum string name_ = text(type);
588         private enum string FunctionAs = (name_[$ - 1] == '_') ? (name_[0 .. $ - 1]) : name_;
589     }
590 
591     private template FunctionNew(Type type) {
592         private enum string name_ = text(type);
593         private enum string FunctionNew = (name_[$ - 1] == '_') ? (name_[0 .. $ - 1]) : name_;
594     }
595 
596     //@nogc nothrow:
597     this(this) {
598         godot_variant other = _godot_variant; // source Variant still owns this
599         _godot_api.variant_new_copy(&_godot_variant, &other);
600     }
601 
602     static Variant nil() {
603         Variant v = void;
604         _godot_api.variant_new_nil(&v._godot_variant);
605         return v;
606     }
607 
608     this(in ref Variant other) {
609         _godot_api.variant_new_copy(&_godot_variant, &other._godot_variant);
610     }
611 
612     this(T : typeof(null))(in T nil) {
613         _godot_api.variant_new_nil(&_godot_variant);
614     }
615 
616     ~this() {
617         // TODO: need to check this, causes broken values after several Variant to variant assignments
618         _godot_api.variant_destroy(&_godot_variant);
619     }
620 
621     Type type() const {
622         return cast(Type) _godot_api.variant_get_type(&_godot_variant);
623     }
624 
625     inout(T) as(T : Variant)() inout {
626         return this;
627     }
628 
629     pragma(inline, true)
630     void opAssign(T : typeof(null))(in T nil) {
631         _godot_api.variant_destroy(&_godot_variant);
632         _godot_api.variant_new_nil(&_godot_variant);
633     }
634 
635     pragma(inline, true)
636     void opAssign(T : Variant)(in T other) {
637         _godot_api.variant_destroy(&_godot_variant);
638         _godot_api.variant_new_copy(&_godot_variant, &other._godot_variant);
639     }
640 
641     bool opEquals(in ref Variant other) const {
642         Variant ret;
643         bool valid;
644         evaluate(GDEXTENSION_VARIANT_OP_EQUAL, this, other, ret, valid);
645         return ret.as!bool;
646         //return cast(bool)_godot_api.variant_operator_equal(&_godot_variant, &other._godot_variant);
647     }
648 
649     private void evaluate(int op, ref const Variant a, ref const Variant b, ref Variant ret, ref bool isValid) const {
650         GDExtensionBool res;
651         _godot_api.variant_evaluate(cast(GDExtensionVariantOperator) op, &a._godot_variant, &b._godot_variant, &ret._godot_variant, &res);
652         isValid = !!res;
653     }
654 
655     int opCmp(in ref Variant other) const {
656         Variant res;
657         bool valid;
658         evaluate(GDEXTENSION_VARIANT_OP_EQUAL, this, other, res, valid);
659         if (res.as!bool)
660             return 0;
661         evaluate(GDEXTENSION_VARIANT_OP_LESS, this, other, res, valid);
662         return res.as!bool ? -1 : 1;
663         //if(_godot_api.variant_operator_equal(&_godot_variant, &other._godot_variant))
664         //	return 0;
665         //return _godot_api.variant_operator_less(&_godot_variant, &other._godot_variant)?
666         //	-1 : 1;
667     }
668 
669     bool booleanize() const {
670         return cast(bool) _godot_api.variant_booleanize(&_godot_variant);
671     }
672 
673     auto toString() const {
674         String str = as!String;
675         return str.data;
676     }
677 
678     /// Is this Variant of the specified `type` or of a subclass of `type`?
679     bool isType(GodotType type) const {
680         import std.sumtype : match;
681 
682         return type.match!(
683             (Ref!Script script) {
684             GodotObject o = this.as!GodotObject;
685             if (o == null)
686                 return false;
687             return script.instanceHas(o);
688         },
689             (BuiltInClass object) {
690             GodotObject o = this.as!GodotObject;
691             if (o == null)
692                 return false;
693             // return o.isClass(object.name);
694             return o.isClass(toDString(object.name));
695         },
696             (Type vt) => this.type == vt
697         );
698     }
699 
700     /++
701 	The exact GodotType of the value stored in this Variant.
702 
703 	To check if a Variant is a specific GodotType, use `isType` instead to
704 	account for inheritance.
705 	+/
706     GodotType exactType() const {
707         if (GodotObject o = this.as!GodotObject) {
708             if (Ref!Script s = o.getScript().as!Script)
709                 return GodotType(s);
710             else
711                 return GodotType(BuiltInClass(o.getClass()));
712         } else
713             return GodotType(this.type);
714     }
715 }