1 /**
2 The most important data type in Godot.
3 
4 Copyright:
5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.  
6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)  
7 Copyright (c) 2017-2018 Godot-D contributors  
8 
9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
10 
11 
12 */
13 module godot.core.variant;
14 
15 import godot.c;
16 import godot.core;
17 import godot.object;
18 import godot.d.traits;
19 import godot.d.reference;
20 import godot.script;
21 import godot.d.type;
22 
23 import std.meta, std.traits;
24 import std.conv : text;
25 import std.range;
26 
27 // for tests
28 import godot.node;
29 import godot.resource;
30 
31 // ABI type should probably have its own `version`...
32 version(X86_64)
33 {
34 	version(DigitalMars)
35 	{
36 		version(linux) version = GodotSystemV;
37 		version(OSX) version = GodotSystemV;
38 		version(Posix) version = GodotSystemV;
39 	}
40 }
41 
42 /// User-defined Variant conversions.
43 /// For structs and classes, constructors and member functions can also be used.
44 unittest
45 {
46 	struct A {}
47 	static assert(!Variant.compatible!A);
48 
49 	struct B {}
50 	B to(T : B)(Variant v) { return B(); }
51 	int to(T : Variant)(B b) { return 1; }
52 	static assert(Variant.compatible!B);
53 
54 	struct C
55 	{
56 		this(Variant v) {}
57 		Variant to(T : Variant)() { return Variant(1); }
58 	}
59 	static assert(Variant.compatible!C);
60 
61 	B b;
62 	C c;
63 
64 	Variant vb = b;
65 	Variant vc = c;
66 
67 	b = vb.as!B;
68 	c = vc.as!C;
69 }
70 
71 /**
72 Godot's tagged union type.
73 
74 Primitives, Godot core types, and `GodotObject`-derived classes can be stored in
75 a Variant. Other user-defined D types can be made compatible with Variant by
76 defining `to!CustomType(Variant)` and `to!Variant(CustomType)` functions.
77 
78 Properties and method arguments/returns are passed between Godot and D through
79 Variant, so these must use Variant-compatible types.
80 */
81 struct Variant
82 {
83 	package(godot) godot_variant _godot_variant;
84 	
85 	/// 
86 	enum Type
87 	{
88 		nil,
89 		
90 		// atomic types
91 		bool_,
92 		int_,
93 		real_,
94 		string,
95 		
96 		// math types
97 		
98 		vector2,// 5
99 		rect2,
100 		vector3,
101 		transform2d,
102 		plane,
103 		quat,// 10
104 		aabb,
105 		basis,
106 		transform,
107 		
108 		// misc types
109 		color,
110 		node_path,// 15
111 		rid,
112 		object,
113 		dictionary,
114 		array,
115 		
116 		// arrays
117 		pool_byte_array,// 20
118 		pool_int_array,
119 		pool_real_array,
120 		pool_string_array,
121 		pool_vector2_array,
122 		pool_vector3_array,// 25
123 		pool_color_array,
124 	}
125 	
126 	/// GDNative type that gets passed to the C functions
127 	alias InternalType = AliasSeq!
128 	(
129 		typeof(null),
130 		
131 		godot_bool,
132 		long,
133 		double,
134 		godot_string,
135 		
136 		godot_vector2,
137 		godot_rect2,
138 		godot_vector3,
139 		godot_transform2d,
140 		godot_plane,
141 		godot_quat,
142 		godot_aabb,
143 		godot_basis,
144 		godot_transform,
145 		
146 		godot_color,
147 		godot_node_path,
148 		godot_rid,
149 		godot_object,
150 		godot_dictionary,
151 		godot_array,
152 		
153 		godot_pool_byte_array,
154 		godot_pool_int_array,
155 		godot_pool_real_array,
156 		godot_pool_string_array,
157 		godot_pool_vector2_array,
158 		godot_pool_vector3_array,
159 		godot_pool_color_array,
160 	);
161 	
162 	/// D type that this Variant implementation uses
163 	alias DType = AliasSeq!
164 	(
165 		typeof(null),
166 		
167 		bool,
168 		long,
169 		double,
170 		String,
171 		
172 		Vector2,// 5
173 		Rect2,
174 		Vector3,
175 		Transform2D,
176 		Plane,
177 		Quat,// 10
178 		AABB,
179 		Basis,
180 		Transform,
181 		
182 		// misc types
183 		Color,
184 		NodePath,// 15
185 		RID,
186 		GodotObject,
187 		Dictionary,
188 		Array,
189 		
190 		// arrays
191 		PoolByteArray,// 20
192 		PoolIntArray,
193 		PoolRealArray,
194 		PoolStringArray,
195 		PoolVector2Array,
196 		PoolVector3Array,// 25
197 		PoolColorArray,
198 	);
199 	
200 	/// 
201 	enum Operator
202 	{
203 		//comparation
204 		equal,
205 		notEqual,
206 		less,
207 		lessEqual,
208 		greater,
209 		greaterEqual,
210 
211 		//mathematic
212 		add,
213 		substract,
214 		multiply,
215 		divide,
216 		negate,
217 		positive,
218 		modulus,
219 		stringConcat,
220 
221 		//bitwise
222 		shiftLeft,
223 		shiftRight,
224 		bitAnd,
225 		bitOr,
226 		bitXor,
227 		bitNegate,
228 
229 		//logic
230 		and,
231 		or,
232 		xor,
233 		not,
234 
235 		//containment
236 		in_
237 	}
238 	
239 	private enum bool implicit(Src, Dest) = is(Src : Dest) || isImplicitlyConvertible!(Src, Dest);
240 
241 	private static GodotObject objectToGodot(T)(T o)
242 	{
243 		return o.getGodotObject;
244 	}
245 
246 	// Conversions for non-core types provided by Variant.
247 	// Lower priority than user-defined `to` functions, to allow overriding
248 	// default blanket implementations.
249 	private alias internalAs(T) = (Variant v) => v.as!T;
250 	private enum bool hasInternalAs(T) = __traits(compiles, internalAs!T);
251 	private alias internalFrom(T) = (T t) => Variant.from(t);
252 	private enum bool hasInternalFrom(T) = __traits(compiles, internalFrom!T);
253 
254 	///
255 	T as(T)() const if(isStaticArray!T && compatibleFromGodot!(ElementType!T))
256 	{
257 		return as!Array.as!T;
258 	}
259 
260 	/// 
261 	T as(T)() const if((isGodotClass!T && !is(T == GodotObject)) || is(T : Ref!U, U))
262 	{
263 		GodotObject o = cast()(as!GodotObject);
264 		return o.as!T;
265 	}
266 
267 	///
268 	static Array from(T)(T t) if((isForwardRange!T || isStaticArray!T) && compatibleToGodot!(ElementType!T))
269 	{
270 		return Array.from(t);
271 	}
272 
273 	///
274 	GodotType as(T : GodotType)() const
275 	{
276 		if(type == Type.object)
277 		{
278 			Ref!Script s = as!Script;
279 			if(s) return GodotType(s);
280 			else return GodotType.init;
281 		}
282 		else if(type == Type..string) return GodotType(BuiltInClass(as!String));
283 		else if(type == Type.int_) return GodotType(cast(Variant.Type)(as!int));
284 		else return GodotType.init;
285 	}
286 
287 	///
288 	static Variant from(T : GodotType)(T t)
289 	{
290 		import sumtype : match;
291 		Variant ret;
292 		t.match!(
293 			(Variant.Type t) { ret = cast(int)t; },
294 			(BuiltInClass c) { ret = c.name; },
295 			(Ref!Script s) { ret = s; }
296 		);
297 		return ret;
298 	}
299 
300 	static assert(hasInternalAs!Node, internalAs!Node);
301 	static assert(hasInternalAs!(Ref!Resource), internalAs!(Ref!Resource));
302 	static assert(!hasInternalAs!Object); // `directlyCompatible` types not handled by internalAs
303 	static assert(hasInternalAs!(int[4]), internalAs!(int[4]));
304 	static assert(hasInternalFrom!(int[4]), internalFrom!(int[4]));
305 	static assert(!hasInternalAs!(int[]));
306 	static assert(hasInternalFrom!(int[]), internalFrom!(int[]));
307 	static assert(hasInternalAs!GodotType, internalAs!GodotType);
308 	static assert(hasInternalFrom!GodotType, internalFrom!GodotType);
309 	static assert(compatible!GodotType);
310 
311 	private template getToVariantFunction(T)
312 	{
313 		mixin("import " ~ moduleName!T ~ ";");
314 		alias getToVariantFunction = (T t){ Variant v = t.to!Variant; return v; };
315 	}
316 	enum bool hasToVariantFunction(T) = __traits(compiles, getToVariantFunction!T);
317 
318 	private template getVariantConstructor(T)
319 	{
320 		alias getVariantConstructor = (Variant v) => T(v);
321 	}
322 	enum bool hasVariantConstructor(T) = __traits(compiles, getVariantConstructor!T);
323 
324 	template getFromVariantFunction(T)
325 	{
326 		mixin("import " ~ moduleName!T ~ ";");
327 		alias getFromVariantFunction = (Variant v){ T ret = v.to!T; return ret; };
328 	}
329 	enum bool hasFromVariantFunction(T) = __traits(compiles, getFromVariantFunction!T);
330 	
331 	/// function to convert T to an equivalent Godot type
332 	template conversionToGodot(T)
333 	{
334 		static if(isGodotClass!T) alias conversionToGodot = objectToGodot!T;
335 		else static if(is(T : GodotStringLiteral!s, string s)) alias conversionToGodot = (T t) => t.str();
336 		else static if(is(T : Ref!U, U)) alias conversionToGodot = objectToGodot!U;
337 		else static if(isIntegral!T) alias conversionToGodot = (T t) => cast(long)t;
338 		else static if(isFloatingPoint!T) alias conversionToGodot = (T t) => cast(double)t;
339 		else static if(implicit!(T, const(char)[]) || implicit!(T, const(char)*))
340 			alias conversionToGodot = (T t) => String(t);
341 		else static if(hasToVariantFunction!T)
342 		{
343 			alias conversionToGodot = getToVariantFunction!T;
344 		}
345 		else static if(hasInternalFrom!T) alias conversionToGodot = internalFrom!T;
346 		else alias conversionToGodot = void; // none
347 	}
348 	enum bool convertsToGodot(T) = isCallable!(conversionToGodot!T);
349 	alias conversionToGodotType(T) = Unqual!(ReturnType!(conversionToGodot!T));
350 	
351 	/// function to convert a Godot-compatible type to T
352 	template conversionFromGodot(T)
353 	{
354 		static if(isIntegral!T) alias conversionFromGodot = (long v) => cast(T)v;
355 		else static if(isFloatingPoint!T) alias conversionFromGodot = (double v) => cast(T)v;
356 		else static if(hasVariantConstructor!T)
357 		{
358 			alias conversionFromGodot = getVariantConstructor!T;
359 		}
360 		else static if(hasFromVariantFunction!T)
361 		{
362 			alias conversionFromGodot = getFromVariantFunction!T;
363 		}
364 		else alias conversionFromGodot = void;
365 	}
366 	enum bool convertsFromGodot(T) = isCallable!(conversionFromGodot!T);
367 	alias conversionFromGodotType(T) = Unqual!(Parameters!(conversionFromGodot!T)[0]);
368 	
369 	enum bool directlyCompatible(T) = staticIndexOf!(Unqual!T, DType) != -1;
370 	template compatibleToGodot(T)
371 	{
372 		static if(directlyCompatible!T) enum bool compatibleToGodot = true;
373 		else enum bool compatibleToGodot = convertsToGodot!T;
374 	}
375 	template compatibleFromGodot(T)
376 	{
377 		static if(directlyCompatible!T) enum bool compatibleFromGodot = true;
378 		else static if(hasInternalAs!T) enum bool compatibleFromGodot = true;
379 		else enum bool compatibleFromGodot = convertsFromGodot!T;
380 	}
381 	enum bool compatible(R) = compatibleToGodot!(R) && compatibleFromGodot!(R);
382 	
383 	
384 	/// All target Variant.Types that T could implicitly convert to, as indices
385 	private template implicitTargetIndices(T)
386 	{
387 		private enum bool _implicit(size_t di) = implicit!(T, DType[di]);
388 		alias implicitTargetIndices = Filter!(_implicit, aliasSeqOf!(iota(DType.length)));
389 	}
390 	
391 	/++
392 	Get the Variant.Type of a compatible D type. Incompatible types return nil.
393 	+/
394 	public template variantTypeOf(T)
395 	{
396 		import std.traits, godot;
397 		
398 		static if(directlyCompatible!T)
399 		{
400 			enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(Unqual!T, DType)];
401 		}
402 		else static if(convertsToGodot!T)
403 		{
404 			static if(is(conversionToGodotType!T : Variant)) enum Type variantTypeOf = Type.nil;
405 			else enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(
406 				conversionToGodotType!T, DType)];
407 		}
408 		else enum Type variantTypeOf = Type.nil; // so the template always returns a Type
409 	}
410 	
411 	/// 
412 	R as(R)() const if(!is(R == Variant) && !is(R==typeof(null)) && (convertsFromGodot!R || directlyCompatible!R))
413 	{
414 		static if(directlyCompatible!R) enum VarType = variantTypeOf!R;
415 		else static if(is(conversionFromGodotType!R : Variant)) enum VarType = Type.nil;
416 		else enum VarType = EnumMembers!Type[staticIndexOf!(conversionFromGodotType!R, DType)];
417 		
418 		// HACK workaround for DMD issue #5570
419 		version(GodotSystemV) enum sV = true;
420 		else enum sV = false;
421 		static if(VarType == Type.vector3 && sV)
422 		{
423 			godot_vector3 ret = void;
424 			void* _func = cast(void*)_godot_api.godot_variant_as_vector3;
425 			void* _this = cast(void*)&this;
426 			
427 			asm @nogc nothrow
428 			{
429 				mov RDI, _this;
430 				call _func;
431 				
432 				mov ret[0], RAX;
433 				mov ret[8], EDX;
434 			}
435 			return *cast(Vector3*)&ret;
436 		}
437 		else static if(VarType == Type.nil)
438 		{
439 			return conversionFromGodot!R(this);
440 		}
441 		else
442 		{
443 			DType[VarType] ret = void;
444 			*cast(InternalType[VarType]*)&ret = mixin("_godot_api.godot_variant_as_"~FunctionAs!VarType~"(&_godot_variant)");
445 
446 			static if(directlyCompatible!R) return ret;
447 			else
448 			{
449 				return conversionFromGodot!R(ret);
450 			}
451 		}
452 	}
453 	
454 	this(R)(auto ref R input) if(!is(R : Variant) && !is(R : typeof(null)))
455 	{
456 		static assert(compatibleToGodot!R, R.stringof~" isn't compatible with Variant.");
457 		enum VarType = variantTypeOf!R;
458 
459 		static if(VarType == Type.nil)
460 		{
461 			this = conversionToGodot!R(input);
462 		}
463 		else
464 		{
465 			mixin("auto Fn = _godot_api.godot_variant_new_"~FunctionNew!VarType~";");
466 			alias PassType = Parameters!Fn[1]; // second param is the value
467 
468 			alias IT = InternalType[VarType];
469 
470 			// handle explicit conversions
471 			static if(directlyCompatible!R) alias inputConv = input;
472 			else auto inputConv = conversionToGodot!R(input);
473 
474 			static if(is(IT == Unqual!PassType)) Fn(&_godot_variant, cast(IT)inputConv); // value
475 			else Fn(&_godot_variant, cast(IT*)&inputConv); // pointer
476 		}
477 	}
478 	
479 	pragma(inline, true)
480 	void opAssign(T)(in auto ref T input) if(!is(T : Variant) && !is(T : typeof(null)))
481 	{
482 		import std.conv : emplace;
483 		
484 		_godot_api.godot_variant_destroy(&_godot_variant);
485 		emplace!(Variant)(&this, input);
486 	}
487 	
488 	static assert(allSatisfy!(compatible, DType));
489 	static assert(!compatible!Object); // D Object
490 
491 	static assert(directlyCompatible!GodotObject);
492 	static assert(directlyCompatible!(const(GodotObject)));
493 	static assert(!directlyCompatible!Node);
494 	static assert(compatibleFromGodot!Node);
495 	static assert(compatibleToGodot!Node);
496 	static assert(compatibleFromGodot!(const(Node)));
497 	static assert(compatibleToGodot!(const(Node)));
498 	static assert(!directlyCompatible!(Ref!Resource));
499 	static assert(compatibleFromGodot!(Ref!Resource));
500 	static assert(compatibleToGodot!(Ref!Resource));
501 	static assert(compatibleFromGodot!(const(Ref!Resource)));
502 	static assert(compatibleToGodot!(const(Ref!Resource)));
503 
504 	private template FunctionAs(Type type)
505 	{
506 		private enum string name_ = text(type);
507 		private enum string FunctionAs = (name_[$-1]=='_')?(name_[0..$-1]):name_;
508 	}
509 	private template FunctionNew(Type type)
510 	{
511 		private enum string name_ = text(type);
512 		private enum string FunctionNew = (name_[$-1]=='_')?(name_[0..$-1]):name_;
513 	}
514 	
515 	@nogc nothrow:
516 	this(this)
517 	{
518 		godot_variant other = _godot_variant; // source Variant still owns this
519 		_godot_api.godot_variant_new_copy(&_godot_variant, &other);
520 	}
521 	
522 	static Variant nil()
523 	{
524 		Variant v = void;
525 		_godot_api.godot_variant_new_nil(&v._godot_variant);
526 		return v;
527 	}
528 	
529 	this(in ref Variant other)
530 	{
531 		_godot_api.godot_variant_new_copy(&_godot_variant, &other._godot_variant);
532 	}
533 	
534 	this(T : typeof(null))(in T nil)
535 	{
536 		_godot_api.godot_variant_new_nil(&_godot_variant);
537 	}
538 	
539 	~this()
540 	{
541 		_godot_api.godot_variant_destroy(&_godot_variant);
542 	}
543 	
544 	Type type() const
545 	{
546 		return cast(Type)_godot_api.godot_variant_get_type(&_godot_variant);
547 	}
548 	
549 	inout(T) as(T : Variant)() inout { return this; }
550 	
551 	pragma(inline, true)
552 	void opAssign(T : typeof(null))(in T nil)
553 	{
554 		_godot_api.godot_variant_destroy(&_godot_variant);
555 		_godot_api.godot_variant_new_nil(&_godot_variant);
556 	}
557 	
558 	pragma(inline, true)
559 	void opAssign(T : Variant)(in T other)
560 	{
561 		_godot_api.godot_variant_destroy(&_godot_variant);
562 		_godot_api.godot_variant_new_copy(&_godot_variant, &other._godot_variant);
563 	}
564 	
565 	bool opEquals(in ref Variant other) const
566 	{
567 		return cast(bool)_godot_api.godot_variant_operator_equal(&_godot_variant, &other._godot_variant);
568 	}
569 	
570 	int opCmp(in ref Variant other) const
571 	{
572 		if(_godot_api.godot_variant_operator_equal(&_godot_variant, &other._godot_variant))
573 			return 0;
574 		return _godot_api.godot_variant_operator_less(&_godot_variant, &other._godot_variant)?
575 			-1 : 1;
576 	}
577 	
578 	bool booleanize() const
579 	{
580 		return cast(bool)_godot_api.godot_variant_booleanize(&_godot_variant);
581 	}
582 	
583 	auto toString() const
584 	{
585 		String str = as!String;
586 		return str.data;
587 	}
588 
589 	/// Is this Variant of the specified `type` or of a subclass of `type`?
590 	bool isType(GodotType type) const
591 	{
592 		import sumtype : match;
593 		return type.match!(
594 			(Ref!Script script) {
595 				GodotObject o = this.as!GodotObject;
596 				if(o == null) return false;
597 				return script.instanceHas(o);
598 			},
599 			(BuiltInClass object) {
600 				GodotObject o = this.as!GodotObject;
601 				if(o == null) return false;
602 				return o.isClass(object.name);
603 			},
604 			(Type vt) => this.type == vt
605 		);
606 	}
607 
608 	/++
609 	The exact GodotType of the value stored in this Variant.
610 
611 	To check if a Variant is a specific GodotType, use `isType` instead to
612 	account for inheritance.
613 	+/
614 	GodotType exactType() const
615 	{
616 		if(GodotObject o = this.as!GodotObject)
617 		{
618 			if(Ref!Script s = o.getScript().as!Script) return GodotType(s);
619 			else return GodotType(BuiltInClass(o.getClass()));
620 		}
621 		else return GodotType(this.type);
622 	}
623 }
624