1 module api.methods; 2 3 import godotutil..string; 4 import api.classes, api.enums, api.util; 5 6 import asdf; 7 8 import std.range; 9 import std.algorithm.searching, std.algorithm.iteration; 10 import std.path; 11 import std.conv : text; 12 import std..string; 13 14 15 16 17 18 class GodotMethod 19 { 20 string name; 21 Type return_type; 22 bool is_editor; 23 bool is_noscript; 24 bool is_const; 25 bool is_virtual; 26 bool has_varargs; 27 bool is_from_script; 28 GodotArgument[] arguments; 29 30 void finalizeDeserialization(Asdf data) 31 { 32 foreach(i, ref a; arguments) 33 { 34 a.index = i; 35 a.parent = this; 36 } 37 } 38 39 @serializationIgnore: 40 GodotClass parent; 41 42 string ddoc; 43 44 bool same(in GodotMethod other) const 45 { 46 return name == other.name && is_const == other.is_const; 47 } 48 49 string templateArgsString() const 50 { 51 string ret = ""; 52 bool first = true; // track first arg to skip comma 53 foreach(i, ref a; arguments) 54 { 55 if(a.type.acceptImplicit) 56 { 57 if(first) first = false; 58 else ret ~= ", "; 59 ret ~= text(a.type.godot, "Arg", i); 60 } 61 } 62 if(has_varargs) 63 { 64 if(!first) ret ~= ", "; 65 ret ~= "VarArgs..."; 66 } 67 // template parens only if it actually is a template 68 if(ret.length != 0) ret = text("(", ret, ")"); 69 return ret; 70 } 71 72 string argsString() const 73 { 74 string ret = "("; 75 bool hasDefault = false; 76 foreach(i, ref a; arguments) 77 { 78 if(i != 0) ret ~= ", "; 79 if(a.type.acceptImplicit) ret ~= text(a.type.dCallParamPrefix, 80 a.type.godot, "Arg", i); 81 else ret ~= text(a.type.dCallParamPrefix, a.type.d); 82 83 ret ~= " " ~ a.name.escapeD; 84 if(a.has_default_value || hasDefault) 85 { 86 ret ~= " = " ~ escapeDefault(a.type, a.default_value); 87 } 88 } 89 if(has_varargs) 90 { 91 if(arguments.length != 0) ret ~= ", "; 92 ret ~= "VarArgs varArgs"; 93 } 94 ret ~= ")"; 95 return ret; 96 } 97 98 string binding() const 99 { 100 string ret; 101 102 ret ~= "\t\t@GodotName(\""~name~"\") GodotMethod!("~return_type.d; 103 foreach(ai, const a; arguments) 104 { 105 ret ~= ", " ~ a.type.d; 106 } 107 if(has_varargs) ret ~= ", GodotVarArgs"; 108 ret ~= ") " ~ name.snakeToCamel.escapeD ~ ";\n"; 109 110 return ret; 111 } 112 113 string source() const 114 { 115 string ret; 116 117 ret ~= "\t/**\n\t"~ddoc.replace("\n", "\n\t")~"\n\t*/\n"; 118 ret ~= "\t"; 119 ret ~= return_type.dRef~" "; 120 // none of the types (Classes/Core/Primitive) are pointers in D 121 // Classes are reference types; the others are passed by value. 122 ret ~= name.snakeToCamel.escapeD; 123 124 ret ~= templateArgsString; 125 ret ~= argsString; 126 127 if(is_const) ret ~= " const"; 128 else if(name == "callv" && parent.name.godot == "Object") ret ~= " const"; /// HACK 129 ret ~= "\n\t{\n"; 130 131 // implementation 132 if(is_virtual || has_varargs) 133 { 134 ret ~= "\t\tArray _GODOT_args = Array.make();\n"; 135 foreach(const a; arguments) 136 { 137 ret ~= "\t\t_GODOT_args.append("~escapeD(a.name)~");\n"; 138 } 139 140 if(has_varargs) 141 { 142 ret ~= "\t\tforeach(vai, VA; VarArgs)\n\t\t{\n"; 143 // TODO: check that VA can convert to Variant 144 ret ~= "\t\t\t_GODOT_args.append(varArgs[vai]);\n"; 145 ret ~= "\t\t}\n"; 146 } 147 148 ret ~= "\t\tString _GODOT_method_name = String(\""~name~"\");\n"; 149 150 ret ~= "\t\t"; 151 if(return_type.d != "void") ret ~= "return "; 152 ret ~= "this.callv(_GODOT_method_name, _GODOT_args)"; 153 if(return_type.d != "void" && return_type.d != "Variant") 154 ret ~= ".as!(RefOrT!"~return_type.d~")"; 155 ret ~= ";\n"; 156 } // end varargs/virtual impl 157 else 158 { 159 ret ~= "\t\tcheckClassBinding!(typeof(this))();\n"; 160 /// ".bind(\"" parent.name.godot ~ "\", \"" ~ name ~ "\");\n"; 161 ret ~= "\t\t"; 162 if(return_type.d != "void") ret ~= "return "; 163 ret ~= "ptrcall!(" ~ return_type.d ~ ")(GDNativeClassBinding." 164 ~ name.snakeToCamel.escapeD ~ ", _godot_object"; 165 foreach(ai, const a; arguments) 166 { 167 ret ~= ", "~a.name.escapeD; 168 } 169 ret ~= ");\n"; 170 } // end normal method impl 171 172 ret ~= "\t}\n"; 173 174 return ret; 175 } 176 } 177 178 struct GodotArgument 179 { 180 string name; 181 Type type; 182 bool has_default_value; 183 string default_value; 184 185 @serializationIgnore: 186 187 size_t index; 188 GodotMethod parent; 189 } 190 191 class GodotProperty 192 { 193 string name; 194 Type type; 195 string getter, setter; 196 int index; 197 198 @serializationIgnore: 199 200 string ddoc; 201 202 string getterSource(in GodotMethod m) const 203 { 204 string ret; 205 ret ~= "\t/**\n\t" ~ ddoc.replace("\n", "\n\t") ~ "\n\t*/\n"; 206 ret ~= "\t@property " ~ m.return_type.d ~ " " ~ name.replace("/","_").snakeToCamel.escapeD ~ "()\n\t{\n"; /// TODO: const? 207 ret ~= "\t\treturn " ~ getter.snakeToCamel.escapeD ~ "("; 208 if(index != -1) ret ~= text(index); 209 ret ~= ");\n"; 210 ret ~= "\t}\n"; 211 return ret; 212 } 213 string setterSource(in GodotMethod m) const 214 { 215 string ret; 216 ret ~= "\t/// ditto\n"; 217 ret ~= "\t@property void " ~ name.replace("/","_").snakeToCamel.escapeD ~ "(" ~ m.arguments[$-1].type.d ~ " v)\n\t{\n"; 218 ret ~= "\t\t" ~ setter.snakeToCamel.escapeD ~ "("; 219 if(index != -1) ret ~= text(index) ~ ", "; 220 ret ~= "v);\n"; 221 ret ~= "\t}\n"; 222 return ret; 223 } 224 } 225 226