1 module command.uda; 2 3 alias CommandType = string delegate(string[]); 4 5 struct CmdTableEntry { 6 CommandType cmd; 7 string desc; 8 int minArgs; 9 int maxArgs; 10 } 11 12 struct CommandNamespace { 13 string name; 14 } 15 16 struct Command { 17 string name; 18 string desc = ""; 19 int minArgs = 0; 20 int maxArgs = 0; 21 } 22 23 struct TypedCommand { 24 string name; 25 string desc; 26 int minArgs = 0; 27 int maxArgs = 0; 28 } 29 30 mixin template RegisterModule(T) 31 if (is(T == class) || is(T == struct)) { 32 mixin("__gshared T " ~ T.mangleof ~ "Singleton;"); 33 shared static this() { 34 import command : gCommandInterpreter; 35 import std.traits; 36 mixin(T.mangleof ~ "Singleton = new T();"); 37 static foreach(m; __traits(allMembers, T)) {{ 38 enum _namespace = () { 39 static if (hasUDA!(__traits(getMember, T, m), CommandNamespace)) { 40 return getUDAs!(__traits(getMember, T, m), CommandNamespace)[0].name; 41 } else { 42 return "global"; 43 } 44 }(); 45 enum cmdName = () { 46 static if (hasUDA!(__traits(getMember, T, m), Command)) { 47 return getUDAs!(__traits(getMember, T, m), Command)[0].name; 48 } else static if (hasUDA!(__traits(getMember, T, m), TypedCommand)) { 49 return getUDAs!(__traits(getMember, T, m), TypedCommand)[0].name; 50 } else { 51 return ""; 52 } 53 }(); 54 static if (hasUDA!(__traits(getMember, T, m), Command)) { 55 pragma(msg, "command " ~ cmdName ~ ", namespace: " ~ _namespace); 56 gCommandInterpreter.registerCommand(_namespace, getUDAs!(__traits(getMember, T, m), Command), mixin("&" ~ T.mangleof ~ "Singleton." ~ m)); 57 } else static if (hasUDA!(__traits(getMember, T, m), TypedCommand)) { 58 pragma(msg, "typed command " ~ cmdName ~ ", namespace: " ~ _namespace); 59 // a little ugly, but it works 60 enum typeConversionShim = (string[] args) { 61 enum _paramCount = Parameters!(__traits(getMember, T, m)).length; 62 import std.conv : to; 63 /* 64 XXX: this is also a hack, but it's the easiest way to generate the mixin that will automatically 65 convert string arguments to the desired arguments 66 67 TODO: overhaul the parser to emit Variants so we don't have to do this 68 string conversion nightmare 69 */ 70 71 enum _paramList = () { 72 string list = "("; 73 static foreach(i, param; Parameters!(__traits(getMember, T, m))) { 74 static assert(__traits(isPOD, param), "Cannot coerce arguments to complex types (struct, class, etc)"); 75 76 // XXX: this is a hack 77 list ~= "to!(" ~ param.stringof ~ ")(args[" ~ to!string(i) ~ "])"; 78 static if (i == (_paramCount - 1)) { 79 list ~= ")"; 80 } else { 81 list ~= ", "; 82 } 83 } 84 return list; 85 }(); 86 ReturnType!(__traits(getMember, T, m)) val = mixin(T.mangleof ~ "Singleton." ~ m ~ _paramList); 87 return to!string(val); 88 }; 89 gCommandInterpreter.registerTypedCommand(_namespace, getUDAs!(__traits(getMember, T, m), TypedCommand), typeConversionShim); 90 } 91 }} 92 } 93 }