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.meta;
19 import godot.d.reference;
20 
21 import std.meta, std.traits;
22 import std.conv : text;
23 import std.range;
24 
25 // ABI type should probably have its own `version`...
26 version(X86_64)
27 {
28 	version(DigitalMars)
29 	{
30 		version(linux) version = GodotSystemV;
31 		version(OSX) version = GodotSystemV;
32 		version(Posix) version = GodotSystemV;
33 	}
34 }
35 
36 /**
37 A Variant takes up only 20 bytes and can store almost any engine datatype inside of it. Variants are rarely used to hold information for long periods of time, instead they are used mainly for communication, editing, serialization and moving data around.
38 */
39 struct Variant
40 {
41 	@nogc nothrow:
42 	
43 	package(godot) godot_variant _godot_variant;
44 	
45 	/// 
46 	enum Type
47 	{
48 		nil,
49 		
50 		// atomic types
51 		bool_,
52 		int_,
53 		real_,
54 		string,
55 		
56 		// math types
57 		
58 		vector2,// 5
59 		rect2,
60 		vector3,
61 		transform2d,
62 		plane,
63 		quat,// 10
64 		aabb,
65 		basis,
66 		transform,
67 		
68 		// misc types
69 		color,
70 		node_path,// 15
71 		rid,
72 		object,
73 		dictionary,
74 		array,
75 		
76 		// arrays
77 		pool_byte_array,// 20
78 		pool_int_array,
79 		pool_real_array,
80 		pool_string_array,
81 		pool_vector2_array,
82 		pool_vector3_array,// 25
83 		pool_color_array,
84 	}
85 	
86 	/// GDNative type that gets passed to the C functions
87 	alias InternalType = AliasSeq!
88 	(
89 		typeof(null),
90 		
91 		godot_bool,
92 		long,
93 		double,
94 		godot_string,
95 		
96 		godot_vector2,
97 		godot_rect2,
98 		godot_vector3,
99 		godot_transform2d,
100 		godot_plane,
101 		godot_quat,
102 		godot_aabb,
103 		godot_basis,
104 		godot_transform,
105 		
106 		godot_color,
107 		godot_node_path,
108 		godot_rid,
109 		godot_object,
110 		godot_dictionary,
111 		godot_array,
112 		
113 		godot_pool_byte_array,
114 		godot_pool_int_array,
115 		godot_pool_real_array,
116 		godot_pool_string_array,
117 		godot_pool_vector2_array,
118 		godot_pool_vector3_array,
119 		godot_pool_color_array,
120 	);
121 	
122 	/// D type that this Variant implementation uses
123 	alias DType = AliasSeq!
124 	(
125 		typeof(null),
126 		
127 		bool,
128 		long,
129 		double,
130 		String,
131 		
132 		Vector2,// 5
133 		Rect2,
134 		Vector3,
135 		Transform2D,
136 		Plane,
137 		Quat,// 10
138 		AABB,
139 		Basis,
140 		Transform,
141 		
142 		// misc types
143 		Color,
144 		NodePath,// 15
145 		RID,
146 		GodotObject,
147 		Dictionary,
148 		Array,
149 		
150 		// arrays
151 		PoolByteArray,// 20
152 		PoolIntArray,
153 		PoolRealArray,
154 		PoolStringArray,
155 		PoolVector2Array,
156 		PoolVector3Array,// 25
157 		PoolColorArray,
158 	);
159 	
160 	/// 
161 	enum Operator
162 	{
163 		//comparation
164 		equal,
165 		notEqual,
166 		less,
167 		lessEqual,
168 		greater,
169 		greaterEqual,
170 
171 		//mathematic
172 		add,
173 		substract,
174 		multiply,
175 		divide,
176 		negate,
177 		positive,
178 		modulus,
179 		stringConcat,
180 
181 		//bitwise
182 		shiftLeft,
183 		shiftRight,
184 		bitAnd,
185 		bitOr,
186 		bitXor,
187 		bitNegate,
188 
189 		//logic
190 		and,
191 		or,
192 		xor,
193 		not,
194 
195 		//containment
196 		in_
197 	}
198 	
199 	private enum bool implicit(Src, Dest) = is(Src : Dest) || isImplicitlyConvertible!(Src, Dest);
200 
201 	private static GodotObject objectToGodot(T)(T o)
202 	{
203 		return o.getGodotObject;
204 	}
205 	private static R objectFromGodot(R)(in GodotObject o)
206 	{
207 		static if(is(R == const)) alias co = o;
208 		else GodotObject co = cast(GodotObject)o._godot_object; // annoying hack to deal with const
209 		
210 		return co.as!R;
211 	}
212 	
213 	/// function to convert T to an equivalent Godot type
214 	template conversionToGodot(T)
215 	{
216 		static if(isGodotClass!T) alias conversionToGodot = objectToGodot!T;
217 		else static if(is(T : GodotStringLiteral!s, string s)) alias conversionToGodot = (T t) => t.str();
218 		else static if(is(T : Ref!U, U)) alias conversionToGodot = objectToGodot!U;
219 		else static if(isIntegral!T) alias conversionToGodot = (T t) => cast(long)t;
220 		else static if(isFloatingPoint!T) alias conversionToGodot = (T t) => cast(double)t;
221 		else static if(implicit!(T, const(char)[]) || implicit!(T, const(char)*))
222 			alias conversionToGodot = (T t) => String(t);
223 		else static if((isForwardRange!T || isStaticArray!T) && compatibleToGodot!(ElementType!T))
224 			alias conversionToGodot = (T t)
225 			{
226 				import std.algorithm.iteration;
227 				Array ret = Array.empty_array;
228 				static if(hasLength!T)
229 				{
230 					ret.resize(cast(int)t.length);
231 					foreach(ei, e; t) ret[cast(int)ei] = e;
232 				}
233 				else t.each!(e => ret ~= e);
234 				return ret;
235 			};
236 		else alias conversionToGodot = void; // none
237 	}
238 	enum bool convertsToGodot(T) = isCallable!(conversionToGodot!T);
239 	alias conversionToGodotType(T) = Unqual!(ReturnType!(conversionToGodot!T));
240 	
241 	/// function to convert a Godot-compatible type to T
242 	template conversionFromGodot(T)
243 	{
244 		static if(isGodotClass!T || is(T : Ref!U, U)) alias conversionFromGodot = objectFromGodot!T;
245 		else static if(isIntegral!T) alias conversionFromGodot = (long v) => cast(T)v;
246 		else static if(isFloatingPoint!T) alias conversionFromGodot = (double v) => cast(T)v;
247 		/*
248 		TODO: overhaul this badly-designed conversion system. These should be
249 		moved out of Variant, into conversion functions on the core types themselves.
250 		*/
251 		else static if(isStaticArray!T && compatibleFromGodot!(ElementType!T))
252 			alias conversionFromGodot = (in Array arr)
253 			{
254 				if(arr.length == T.length)
255 				{
256 					T ret;
257 					foreach(i; 0..T.length) ret[i] = (arr[i]).as!(ElementType!T);
258 					return ret;
259 				}
260 				else assert(0, "Array length doesn't match static array "~T.stringof);
261 			};
262 		else alias conversionFromGodot = void;
263 	}
264 	enum bool convertsFromGodot(T) = isCallable!(conversionFromGodot!T);
265 	alias conversionFromGodotType(T) = Unqual!(Parameters!(conversionFromGodot!T)[0]);
266 	
267 	enum bool directlyCompatible(T) = staticIndexOf!(Unqual!T, DType) != -1;
268 	template compatibleToGodot(T)
269 	{
270 		static if(directlyCompatible!T) enum bool compatibleToGodot = true;
271 		else enum bool compatibleToGodot = convertsToGodot!T;
272 	}
273 	template compatibleFromGodot(T)
274 	{
275 		static if(directlyCompatible!T) enum bool compatibleFromGodot = true;
276 		else enum bool compatibleFromGodot = convertsFromGodot!T;
277 	}
278 	enum bool compatible(R) = compatibleToGodot!(R) && compatibleFromGodot!(R);
279 	
280 	
281 	/// All target Variant.Types that T could implicitly convert to, as indices
282 	private template implicitTargetIndices(T)
283 	{
284 		private enum bool _implicit(size_t di) = implicit!(T, DType[di]);
285 		alias implicitTargetIndices = Filter!(_implicit, aliasSeqOf!(iota(DType.length)));
286 	}
287 	
288 	/++
289 	Get the Variant.Type of a compatible D type. Incompatible types return nil.
290 	+/
291 	public template variantTypeOf(T)
292 	{
293 		import std.traits, godot;
294 		
295 		static if(directlyCompatible!T)
296 		{
297 			enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(Unqual!T, DType)];
298 		}
299 		else static if(convertsToGodot!T)
300 		{
301 			enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(
302 				conversionToGodotType!T, DType)];
303 		}
304 		else enum Type variantTypeOf = Type.nil; // so the template always returns a Type
305 	}
306 	
307 	unittest
308 	{
309 		static assert(allSatisfy!(compatible, DType));
310 		static assert(!compatible!Object); // D Object
311 		
312 		static assert(directlyCompatible!GodotObject);
313 		static assert(directlyCompatible!(const(GodotObject)));
314 		import godot.camera;
315 		static assert(!directlyCompatible!Camera);
316 		static assert(compatibleFromGodot!Camera);
317 		static assert(compatibleToGodot!Camera);
318 		static assert(compatibleFromGodot!(const(Camera)));
319 		static assert(compatibleToGodot!(const(Camera)));
320 	}
321 	
322 	private template FunctionAs(Type type)
323 	{
324 		private enum string name_ = text(type);
325 		private enum string FunctionAs = (name_[$-1]=='_')?(name_[0..$-1]):name_;
326 	}
327 	private template FunctionNew(Type type)
328 	{
329 		private enum string name_ = text(type);
330 		private enum string FunctionNew = (name_[$-1]=='_')?(name_[0..$-1]):name_;
331 	}
332 	
333 	this(this)
334 	{
335 		godot_variant other = _godot_variant; // source Variant still owns this
336 		_godot_api.godot_variant_new_copy(&_godot_variant, &other);
337 	}
338 	
339 	static Variant nil()
340 	{
341 		Variant v = void;
342 		_godot_api.godot_variant_new_nil(&v._godot_variant);
343 		return v;
344 	}
345 	
346 	this(in ref Variant other)
347 	{
348 		_godot_api.godot_variant_new_copy(&_godot_variant, &other._godot_variant);
349 	}
350 	
351 	this(T : typeof(null))(in T nil)
352 	{
353 		_godot_api.godot_variant_new_nil(&_godot_variant);
354 	}
355 	
356 	this(R)(auto ref R input) if(!is(R : Variant) && !is(R : typeof(null)))
357 	{
358 		static assert(compatibleToGodot!R, R.stringof~" isn't compatible with Variant.");
359 		enum VarType = variantTypeOf!R;
360 		
361 		mixin("auto Fn = _godot_api.godot_variant_new_"~FunctionNew!VarType~";");
362 		alias PassType = Parameters!Fn[1]; // second param is the value
363 		
364 		alias IT = InternalType[VarType];
365 		
366 		// handle explicit conversions
367 		static if(directlyCompatible!R) alias inputConv = input;
368 		else auto inputConv = conversionToGodot!R(input);
369 		
370 		static if(is(IT == Unqual!PassType)) Fn(&_godot_variant, cast(IT)inputConv); // value
371 		else Fn(&_godot_variant, cast(IT*)&inputConv); // pointer
372 	}
373 	
374 	~this()
375 	{
376 		_godot_api.godot_variant_destroy(&_godot_variant);
377 	}
378 	
379 	Type type() const
380 	{
381 		return cast(Type)_godot_api.godot_variant_get_type(&_godot_variant);
382 	}
383 	
384 	inout(T) as(T : Variant)() inout { return this; }
385 	
386 	R as(R)() const if(!is(R == Variant) && !is(R==typeof(null)) && compatibleFromGodot!R)
387 	{
388 		static if(directlyCompatible!R) enum VarType = variantTypeOf!R;
389 		else enum VarType = EnumMembers!Type[staticIndexOf!(conversionFromGodotType!R, DType)];
390 		
391 		// HACK workaround for DMD issue #5570
392 		version(GodotSystemV) enum sV = true;
393 		else enum sV = false;
394 		static if(VarType == Type.vector3 && sV)
395 		{
396 			godot_vector3 ret = void;
397 			void* _func = cast(void*)_godot_api.godot_variant_as_vector3;
398 			void* _this = cast(void*)&this;
399 			
400 			asm @nogc nothrow
401 			{
402 				mov RDI, _this;
403 				call _func;
404 				
405 				mov ret[0], RAX;
406 				mov ret[8], EDX;
407 			}
408 			return *cast(Vector3*)&ret;
409 		}
410 		else
411 		{
412 			InternalType[VarType] ret = mixin("_godot_api.godot_variant_as_"~FunctionAs!VarType~"(&_godot_variant)");
413 			
414 			static if(directlyCompatible!R) return *cast(DType[VarType]*)&ret;
415 			else
416 			{
417 				return conversionFromGodot!R(*cast(DType[VarType]*)&ret);
418 			}
419 		}
420 	}
421 	
422 	pragma(inline, true)
423 	void opAssign(T)(in auto ref T input) if(!is(T : Variant) && !is(T : typeof(null)))
424 	{
425 		import std.conv : emplace;
426 		
427 		_godot_api.godot_variant_destroy(&_godot_variant);
428 		emplace!(Variant)(&this, input);
429 	}
430 	
431 	pragma(inline, true)
432 	void opAssign(T : typeof(null))(in T nil)
433 	{
434 		_godot_api.godot_variant_destroy(&_godot_variant);
435 		_godot_api.godot_variant_new_nil(&_godot_variant);
436 	}
437 	
438 	pragma(inline, true)
439 	void opAssign(T : Variant)(in T other)
440 	{
441 		_godot_api.godot_variant_destroy(&_godot_variant);
442 		_godot_api.godot_variant_new_copy(&_godot_variant, &other._godot_variant);
443 	}
444 	
445 	bool opEquals(in ref Variant other) const
446 	{
447 		return cast(bool)_godot_api.godot_variant_operator_equal(&_godot_variant, &other._godot_variant);
448 	}
449 	
450 	int opCmp(in ref Variant other) const
451 	{
452 		if(_godot_api.godot_variant_operator_equal(&_godot_variant, &other._godot_variant))
453 			return 0;
454 		return _godot_api.godot_variant_operator_less(&_godot_variant, &other._godot_variant)?
455 			-1 : 1;
456 	}
457 	
458 	bool booleanize() const
459 	{
460 		return cast(bool)_godot_api.godot_variant_booleanize(&_godot_variant);
461 	}
462 	
463 	auto toString() const
464 	{
465 		String str = as!String;
466 		return str.data;
467 	}
468 }
469