1 /// Contains the action functions for arguments. 2 module jaster.cli.infogen.actions; 3 4 import std.format, std.traits, std.typecons : Nullable; 5 import jaster.cli.infogen, jaster.cli.binder, jaster.cli.result; 6 7 // ArgBinder knows how to safely discard UDAs it doesn't care about. 8 private alias getBinderUDAs(alias ArgT) = __traits(getAttributes, ArgT); 9 10 // Now you may think: "But Bradley, this is module-level, why does this need to be marked static?" 11 // 12 // Well now little Timmy, what you don't seem to notice is that D, for some reason, is embedding a "this" pointer (as a consequence of `ArgT` referencing a member field), 13 // however because this is still technically a `function` I can call it *without* providing a context pointer, which has led to some very 14 // interesting errors. 15 16 /// Sets the argument's value via `ArgBinder`. 17 static Result!void actionValueBind(alias CommandT, alias ArgT, alias ArgBinderInstance)(string value, ref CommandT commandInstance) 18 { 19 import std.typecons : Nullable; // Don't ask me why, but I need to repeat the import here for the amalgamation to compile properly. 20 // For some incredibly strange reason, if we don't do this, then `Nullable` evaluated to `void`. 21 22 alias SymbolType = typeof(ArgT); 23 24 static if(isInstanceOf!(Nullable, SymbolType)) 25 { 26 // The Unqual removes the `inout` that `get` uses. 27 alias ResultT = Unqual!(ReturnType!(SymbolType.get)); 28 } 29 else 30 alias ResultT = SymbolType; 31 32 auto result = ArgBinderInstance.bind!(ResultT, getBinderUDAs!ArgT)(value); 33 if(!result.isSuccess) 34 return Result!void.failure(result.asFailure.error); 35 36 mixin("commandInstance.%s = result.asSuccess.value;".format(__traits(identifier, ArgT))); 37 return Result!void.success(); 38 } 39 40 /// Increments the argument's value either by 1, or by the length of `value` if it is not null. 41 static Result!void actionCount(alias CommandT, alias ArgT, alias ArgBinderInstance)(string value, ref CommandT commandInstance) 42 { 43 static assert(__traits(compiles, {typeof(ArgT) a; a++;}), "Type "~typeof(ArgT).stringof~" does not implement the '++' operator."); 44 45 // If parser passes null then the user's input was: -v or -vsome_value 46 // If parser passes value then the user's input was: -vv or -vvv(n+1) 47 const amount = (value is null) ? 1 : value.length; 48 49 // Simplify implementation for user-defined types by only using ++. 50 foreach(i; 0..amount) 51 mixin("commandInstance.%s++;".format(__traits(identifier, ArgT))); 52 53 return Result!void.success(); 54 } 55 56 /// Fails an assert if used. 57 static Result!void dummyAction(alias CommandT)(string value, ref CommandT commandInstance) 58 { 59 assert(false, "This action doesn't do anything."); 60 }