1 /++
2 Compile-time introspection of Godot types
3 +/
4 module godot.d.meta;
5 
6 import godot.d.string;
7 import godot.d.udas;
8 import godot.d.reference;
9 
10 import std.meta, std.traits;
11 
12 import godot.core, godot.c;
13 import godot.object;
14 
15 @nogc nothrow:
16 
17 /++
18 Adds the Ref wrapper to T, if T is a Reference type
19 +/
20 template RefOrT(T)
21 {
22 	import godot.reference;
23 	static if(isGodotClass!T && extends!(T, Reference)) alias RefOrT = Ref!T;
24 	else alias RefOrT = T;
25 }
26 
27 /++
28 Removes the Ref wrapper from R, if present
29 +/
30 template NonRef(R)
31 {
32 	static if(is(R : Ref!T, T)) alias NonRef = T;
33 	else alias NonRef = R;
34 }
35 
36 /++
37 A UDA with which base Godot classes are marked. NOT used by new D classes.
38 +/
39 package(godot) enum GodotBaseClass;
40 
41 /++
42 Determine if T is a class originally from the Godot Engine (but *not* a new D
43 class registered to Godot).
44 +/
45 template isGodotBaseClass(T)
46 {
47 	static if(is(T == struct)) enum bool isGodotBaseClass =
48 		hasUDA!(T, GodotBaseClass);
49 	else enum bool isGodotBaseClass = false;
50 }
51 
52 /++
53 Determine if T is a D native script (extends a Godot base class).
54 +/
55 template extendsGodotBaseClass(T)
56 {
57 	static if(is(T == class) && hasMember!(T, "owner"))
58 	{
59 		enum bool extendsGodotBaseClass = isGodotBaseClass!(typeof(T.owner));
60 	}
61 	else enum bool extendsGodotBaseClass = false;
62 }
63 
64 /++
65 A list of all of T's base classes, both script and C++, ending with GodotObject.
66 
67 Has the same purpose as std.traits.BaseClassesTuple, but accounts for Godot's
68 script inheritance system.
69 +/
70 template GodotBaseClasses(T)
71 {
72 	static if(isGodotBaseClass!T) alias GodotBaseClasses = T.BaseClasses;
73 	else static if(extendsGodotBaseClass!T)
74 	{
75 		import std.traits : BaseClassesTuple;
76 		// the last two D base classes are GodotScript!<Base> and Object.
77 		alias GodotBaseClasses = AliasSeq!(BaseClassesTuple!(Unqual!T)[0..$-2],
78 			GodotClass!T, GodotClass!T.BaseClasses);
79 	}
80 }
81 
82 /++
83 Checks whether R is a subtype of ParentR by Godot's script inheritance system.
84 Both D script and C++ classes are accounted for.
85 If R and ParentR are the same, `extends` is true as well.
86 +/
87 template extends(R, ParentR)
88 {
89 	alias T = NonRef!R;
90 	alias Parent = NonRef!ParentR;
91 	static if(is(Unqual!T : Unqual!Parent)) enum bool extends = true;
92 	else enum bool extends = staticIndexOf!(Unqual!Parent, GodotBaseClasses!T) != -1;
93 }
94 
95 /++
96 Get the Godot class of R (the class of the `owner` for D native scripts)
97 +/
98 template GodotClass(R)
99 {
100 	alias T = NonRef!R;
101 	static if(isGodotBaseClass!T) alias GodotClass = T;
102 	else static if(extendsGodotBaseClass!T) alias GodotClass = typeof(T.owner);
103 }
104 
105 /++
106 Determine if T is any Godot class (base C++ class or D native script, but NOT
107 a godot.core struct)
108 +/
109 enum bool isGodotClass(T) = extendsGodotBaseClass!T || isGodotBaseClass!T;
110 
111 /++
112 Get the C++ Godot Object pointer of either a Godot Object OR a D native script.
113 
114 Useful for generic code.
115 +/
116 GodotClass!T getGodotObject(T)(in T t) if(isGodotClass!T)
117 {
118 	GodotClass!T ret;
119 	ret._godot_object = t.getGDNativeObject;
120 	return ret;
121 }
122 GodotClass!(NonRef!R) getGodotObject(R)(auto ref R r) if(is(R : Ref!U, U)) { return r._reference; }
123 
124 package(godot) godot_object getGDNativeObject(T)(in T t) if(isGodotClass!T)
125 {
126 	static if(isGodotBaseClass!T) return cast(godot_object)t._godot_object;
127 	static if(extendsGodotBaseClass!T)
128 	{
129 		return (t) ? cast(godot_object)t.owner._godot_object : godot_object.init;
130 	}
131 }
132 package(godot) godot_object getGDNativeObject(R)(auto ref R r) if(is(R : Ref!U, U)) { return r._reference._godot_object; }
133 
134 /++
135 Alias to default-constructed T, as an expression.
136 
137 A few Godot core types can't use D's `init` because they need to call a C++
138 constructor through GDNative.
139 +/
140 template godotDefaultInit(T)
141 {
142 	static if(is(T : Array)) alias godotDefaultInit = Alias!(T.empty_array);
143 	else static if(is(T : Dictionary)) alias godotDefaultInit = Alias!(
144 		Dictionary.empty_dictionary);
145 	else alias godotDefaultInit = Alias!(T.init);
146 }
147 
148 package(godot) enum string dName(alias a) = __traits(identifier, a);
149 package(godot) template godotName(alias a)
150 {
151 	alias udas = getUDAs!(a, Rename);
152 	static if(udas.length == 0)
153 	{
154 		version(GodotNoAutomaticNamingConvention) enum string godotName = __traits(identifier, a);
155 		else enum string godotName = __traits(identifier, a).camelToSnake;
156 	}
157 	else
158 	{
159 		static assert(udas.length == 1, "Multiple Rename UDAs on "~
160 			fullyQualifiedName!a~"? Why?");
161 		
162 		static if(is( udas[0] )) static assert(0, "Construct the UDA with a string: @Rename(\"name\")");
163 		else
164 		{
165 			enum Rename uda = udas[0];
166 			enum string godotName = uda.name;
167 		}
168 	}
169 }
170