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 }