1 module api.util;
2 
3 import api.classes;
4 
5 import std.range;
6 import std.algorithm.searching, std.algorithm.iteration;
7 import std.path;
8 import std.conv : text;
9 import std..string;
10 
11 import asdf;
12 
13 class Type
14 {
15 	static Type[string] typesByGodotName;
16 	static Type[string] typesByDName;
17 	
18 	static Type[] enums;
19 	
20 	GodotClass objectClass;
21 	string d;
22 	string godot;
23 	
24 	@property string dRef() const { return isRef ? ("Ref!"~d) : d; }
25 	
26 	Type enumParent;
27 	
28 	//alias d this;
29 	
30 	string moduleName() const
31 	{
32 		if(isPrimitive || isCoreType) return null;
33 		return godot.chompPrefix("_").toLower;
34 	}
35 	
36 	bool isEnum() const
37 	{
38 		return godot.startsWith("enum.");
39 	}
40 	
41 	bool isPrimitive() const
42 	{
43 		if(isEnum) return true;
44 		return only("int", "bool", "real", "float", "void").canFind(godot);
45 	}
46 
47 	bool isCoreType() const
48 	{
49 		auto coreTypes = only("AABB",
50 		                      "Array",
51 		                      "Basis",
52 		                      "Color",
53 		                      "Dictionary",
54 		                      "GodotError",
55 		                      "NodePath",
56 		                      "Plane",
57 		                      "PoolByteArray",
58 		                      "PoolIntArray",
59 		                      "PoolRealArray",
60 		                      "PoolStringArray",
61 		                      "PoolVector2Array",
62 		                      "PoolVector3Array",
63 		                      "PoolColorArray",
64 		                      "Quat",
65 		                      "Rect2",
66 		                      "RID",
67 		                      "String",
68 		                      "Transform",
69 		                      "Transform2D",
70 		                      "Variant",
71 		                      "Vector2",
72 		                      "Vector3");
73 		return coreTypes.canFind(godot);
74 	}
75 	
76 	bool isRef() const
77 	{
78 		return objectClass && objectClass.is_reference;
79 	}
80 
81 	/// type should be taken as template arg by methods to allow implicit conversion in ptrcall
82 	bool acceptImplicit() const
83 	{
84 		auto accept = only("Variant", "NodePath");
85 		return accept.canFind(godot);
86 	}
87 
88 	/// prefix for function parameter of this type
89 	string dCallParamPrefix() const
90 	{
91 		if(isRef) return "";
92 		else if(objectClass) return "";
93 		else return "in ";
94 	}
95 	/// how to pass parameters of this type into ptrcall void** arg
96 	string ptrCallArgPrefix() const
97 	{
98 		if(isPrimitive || isCoreType) return "&";
99 		return "";
100 		//return "cast(godot_object)"; // for both base classes and D classes (through alias this)
101 	}
102 	
103 	
104 	this(string godotName)
105 	{
106 		godot = godotName;
107 		d = godotName.escapeType;
108 	}
109 	static Type get(string godotName)
110 	{
111 		if(!godotName.length) return null; // no type (used in base_class)
112 		if(Type* ptr = godotName in typesByGodotName) return *ptr;
113 		Type ret = new Type(godotName);
114 		
115 		static import api.enums;
116 		if(ret.isEnum)
117 		{
118 			ret.enumParent = get(api.enums.enumParent(godotName));
119 			enums ~= ret;
120 		}
121 		
122 		typesByGodotName[godotName] = ret;
123 		typesByDName[ret.d] = ret;
124 		
125 		return ret;
126 	}
127 	static Type deserialize(ref Asdf asdf)
128 	{
129 		string gn = asdf.get!string(null);
130 		Type ret = get(gn);
131 		return ret;
132 	}
133 }
134 
135 /// the default value to use for an argument if none is provided
136 string emptyDefault(in Type type)
137 {
138 	import std..string;
139 	import std.conv : text;
140 	
141 	switch(type.d)
142 	{
143 		case "String":
144 			return `gs!""`;
145 		case "Dictionary":
146 			return type.d~".make()";
147 		case "Array":
148 			return type.d~".make()";
149 		default: // all default-blittable types
150 		{
151 			return type.d~".init"; // D's default initializer
152 			///return "null";
153 		}
154 	}
155 }
156 
157 /++
158 PoolVector2Array
159 PoolColorArray
160 Array
161 Vector2
162 float
163 Color
164 bool
165 Object
166 PoolVector3Array
167 Vector3
168 Transform2D
169 RID
170 int
171 Transform
172 Rect2
173 String
174 Variant
175 PoolStringArray
176 +/
177 string escapeDefault(in Type type, string arg)
178 {
179 	import std..string;
180 	import std.conv : text;
181 	
182 	if(!arg || arg.length == 0) return emptyDefault(type);
183 	
184 	// parse the defaults in api.json
185 	switch(type.d)
186 	{
187 		case "Color": // "1,1,1,1"
188 			return "Color("~arg~")";
189 		case "bool": // True, False
190 			return arg.toLower;
191 		case "Array": // "[]", "Null" - just use the empty one
192 		case "Dictionary":
193 		case "PoolByteArray":
194 		case "PoolIntArray":
195 		case "PoolRealArray":
196 		case "PoolVector2Array":
197 		case "PoolVector3Array":
198 		case "PoolStringArray":
199 		case "PoolColorArray": // "[PoolColorArray]" - wat?
200 			return emptyDefault(type);
201 		case "Transform": // "1, 0, 0, 0, 1, 0, 0, 0, 1 - 0, 0, 0" TODO: parse this
202 		case "Transform2D":
203 		case "RID": // always empty?
204 			return emptyDefault(type); // D's default initializer
205 		case "Vector2": // "(0, 0)"
206 		case "Vector3":
207 		case "Rect2": // "(0, 0, 0, 0)"
208 		case "AABB":
209 			return type.d~arg;
210 		case "Variant":
211 			if(arg == "Null") return "Variant.nil";
212 			else return arg;
213 		case "String":
214 			return "gs!\""~arg~"\"";
215 		default: // all Object types
216 		{
217 			if(arg == "Null") return emptyDefault(type);
218 			if(arg == "[Object:null]") return emptyDefault(type);
219 			return arg;
220 		}
221 	}
222 }
223 
224 string escapeType(string t)
225 {
226 	import api.enums : qualifyEnumName;
227 	
228 	t = t.chompPrefix("_");
229 	
230 	if(t == "Object") return "GodotObject";
231 	if(t == "Error") return "GodotError";
232 	if(t == "float") return "double";
233 	if(t == "int") return "long";
234 	if(t.startsWith("enum.")) return t.qualifyEnumName;
235 	return t;
236 }
237 
238 string escapeD(string s)
239 {
240 	import std.meta;
241 	import std.uni, std.utf;
242 	/// TODO: there must be a better way of doing this...
243 	/// maybe one of the D parser libraries has a list of keywords and basic types
244 	
245 	if(s.toUTF32[0].isNumber) s = "_"~s; // can't start with a number
246 	
247 	alias keywords = AliasSeq!(
248 		"class",
249 		"interface",
250 		"struct",
251 		"enum",
252 		"bool",
253 		"ubyte",
254 		"byte",
255 		"ushort",
256 		"short",
257 		"uint",
258 		"int",
259 		"ulong",
260 		"long",
261 		"cent", // really?
262 		"ucent",
263 		"float",
264 		"double",
265 		"real",
266 		"char",
267 		"wchar",
268 		"dchar",
269 		"function",
270 		"delegate",
271 		"override",
272 		"default",
273 		"case",
274 		"switch",
275 		"export",
276 		"import",
277 		"template",
278 		"new",
279 		"delete",
280 		"return",
281 		"with",
282 		"align",
283 		"in",
284 		"out",
285 		"ref",
286 		"scope",
287 		"auto",
288 		"init",
289 		"body", // for now at least...
290 	);
291 	switch(s)
292 	{
293 		case "Object": return "GodotObject";
294 		case "Error": return "GodotError";
295 		foreach(kw; keywords)
296 		{
297 			case kw:
298 		}
299 			return "_"~s;
300 		default:
301 			return s;
302 	}
303 }
304