1 /// Contains helpful templates relating to UDAs.
2 module jaster.cli.udas;
3 
4 /++
5  + Gets a single specified `UDA` from the given `Symbol`.
6  +
7  + Assertions:
8  +  If the given `Symbol` has either 0, or more than 1 instances of the specified `UDA`, a detailed error message will be displayed.
9  + ++/
10 template getSingleUDA(alias Symbol, alias UDA)
11 {
12     import std.traits : getUDAs;
13 
14     // Check if they created an instance `@UDA()`
15     //
16     // or if they just attached the type itself `@UDA`
17     static if(__traits(compiles, {enum UDAs = getUDAs!(Symbol, UDA);}))
18         enum UDAs = getUDAs!(Symbol, UDA);
19     else
20         enum UDAs = [UDA.init];
21     
22     static if(UDAs.length == 0)
23         static assert(false, "The symbol `"~Symbol.stringof~"` does not have the `@"~UDA.stringof~"` UDA");
24     else static if(UDAs.length > 1)
25         static assert(false, "The symbol `"~Symbol.stringof~"` contains more than one `@"~UDA.stringof~"` UDA");
26 
27     enum getSingleUDA = UDAs[0];
28 }
29 ///
30 version(unittest)
31 {
32     import jaster.cli.infogen : Command;
33 
34     private struct A {}
35 
36     @Command("One")
37     private struct B {}
38 
39     @Command("One")
40     @Command("Two")
41     private struct C {}
42 }
43 
44 unittest
45 {
46     import jaster.cli.infogen : Command;
47 
48     static assert(!__traits(compiles, getSingleUDA!(A, Command)));
49     static assert(!__traits(compiles, getSingleUDA!(C, Command)));
50     static assert(getSingleUDA!(B, Command).pattern.pattern == "One");
51 }
52 
53 /++
54  + Sometimes code needs to support both `@UDA` and `@UDA()`, so this template is used
55  + to ensure that the given `UDA` is an actual object, not just a type.
56  + ++/
57 template ctorUdaIfNeeded(alias UDA)
58 {
59     import std.traits : isType;
60     static if(isType!UDA)
61         enum ctorUdaIfNeeded = UDA.init;
62     else
63         alias ctorUdaIfNeeded = UDA;
64 }
65 
66 /++
67  + Gets all symbols that have specified UDA from all specified modules
68  + ++/
69 template getSymbolsByUDAInModules(alias attribute, Modules...)
70 {
71     import std.meta: AliasSeq;
72     import std.traits: getSymbolsByUDA;
73 
74     static if(Modules.length == 0)
75     {
76         alias getSymbolsByUDAInModules = AliasSeq!();
77     }
78     else
79     {
80         alias tail = getSymbolsByUDAInModules!(attribute, Modules[1 .. $]);
81 
82         alias getSymbolsByUDAInModules = AliasSeq!(getSymbolsByUDA!(Modules[0], attribute), tail);
83     }
84 }
85 
86 unittest
87 {
88     import std.meta: AliasSeq;
89     import jaster.cli.infogen : Command;
90 
91     static assert(is(getSymbolsByUDAInModules!(Command, jaster.cli.udas) == AliasSeq!(B, C)));
92     static assert(is(getSymbolsByUDAInModules!(Command, jaster.cli.udas, jaster.cli.udas) == AliasSeq!(B, C, B, C)));
93 }