ArgBinder

A static struct providing functionality for binding a string (the argument) to a value, as well as optionally validating it.

Description: The ArgBinder itself does not directly contain functions to bind or validate arguments (e.g arg -> int, arg -> enum, etc.).

Instead, arg binders are user-provided, free-standing functions that are automatically detected from the specified Modules.

For each module passed in the Modules template parameter, the arg binder will search for any free-standing function marked with the @ArgBinderFunc UDA. These functions must follow a specific signature @ArgBinderFunc void myBinder(string arg, ref TYPE value).

The second parameter (marked 'TYPE') can be *any* type that is desired. The type of this second parameter defines which type it will bind/convert the given argument into. The second parameter may also be a template type, if needed, to allow for more generic binders.

For example, the following binder @ArgBinderFunc void argToInt(string arg, ref int value); will be called anytime the arg binder needs to bind the argument into an int value.

When binding a value, you can optionally pass in a set of Validators, which are (typically) struct UDAs that provide a certain interface for validation.

Members

Aliases

AllModules
alias AllModules = AliasSeq!(Modules, jcli)
Undocumented in source.
AllModules
alias AllModules = AliasSeq!(Modules, jaster.cli.binder)
Undocumented in source.

Static functions

bind
Result!T bind(string arg)

Binds the given arg to the value, using the @ArgBinderFunc found by using the 'Lookup Rules' documented in the document comment for ArgBinder.

Parameters

Modules

The modules to look over. Please read the 'Description' and 'Lookup Rules' sections of this documentation comment.

Detailed Description

Lookup Rules

The arg binder functions off of a simple 'First come first served' ruleset.

When looking for a suitable @ArgBinderFunc for the given value type, the following process is taken: * Foreach module in the Modules type parameter (from first to last). * Foreach free-standing function inside of the current module (usually in lexical order). * Do a compile-time check to see if this function can be called with a string as the first parameter, and the value type as the second. * If the check passes, use this function. * Otherwise, continue onto the next function.

This means there is significant meaning in the order that the modules are passed. Because of this, the built-in binders (contained in the same module as this struct) will always be put at the very end of the list, meaning the user has the oppertunity to essentially 'override' any of the built-in binders.

One may ask "Won't that be confusing? How do I know which binder is being used?". My answer, while not perfect, is in non-release builds, the binder will output a debug pragma to give detailed information on which binders are used for which types, and which ones are skipped over (and why they were skipped).

Note that you must also add "JCLI_Verbose" as a version (either in your dub file, or cli, or whatever) for these messages to show up.

While not perfect, this does go over the entire process the arg binder is doing to select which @ArgBinderFunc it will use.

Specific Binders

Instead of using the lookup rules above, you can make use of the ArgBindWith UDA to provide a specific function to perform the binding of an argument.

Validation

Validation structs can be passed via the UDAs template parameter present for the ArgBinder.bind function.

If you are using CommandLineInterface (JCLI's default core), then a field's UDAs are passed through automatically as validator structs.

A validator is simply a struct marked with @ArgValidator that defines either, or both of these function signatures (or compatible signatures):

Result!void onPreValidate(string arg);
Result!void onValidate(VALUE_TYPE value); // Can be templated of course.

A validator containing the onPreValidate function can be used to validate the argument prior to it being ran through an @ArgBinderFunc.

A validator containing the onValidate function can be used to validate the argument after it has been bound by an @ArgBinderFunc.

If validation fails, the vaildator can set the error message with Result!void.failure(). If this is left as null, then one will be automatically generated for you.

By specifying the "JCLI_Verbose" version, the ArgBinder will detail what validators are being used for what types, and for which stages of binding.

Notes: While other parts of this library have special support for Nullable types. This struct doesn't directly have any special behaviour for them, and instead must be built on top of this struct (a templated @ArgBinderFunc just for nullables is totally possible!).

Examples

alias Binder = ArgBinder!(jaster.cli.binder);

// Non-validated bindings.
auto value    = Binder.bind!int("200");
auto strValue = Binder.bind!string("200");

assert(value.asSuccess.value == 200);
assert(strValue.asSuccess.value == "200");

// Validated bindings
@ArgValidator
static struct GreaterThan
{
    import std.traits : isNumeric;
    ulong value;

    Result!void onValidate(T)(T value)
    if(isNumeric!T)
    {
        import std.format : format;

        return value > this.value
        ? Result!void.success()
        : Result!void.failure("Value %s is NOT greater than %s".format(value, this.value));
    }
}

value = Binder.bind!(int, GreaterThan(68))("69");
assert(value.asSuccess.value == 69);

// Failed validation
assert(Binder.bind!(int, GreaterThan(70))("69").isFailure);

Meta