SubCmd

SubcommandParserConfig sub_state

finish : CliBuilder state fromAction toAction, { name : Str, description ? Str, mapper : state -> commonState } -> { name : Str, parser : ArgParser commonState, config : SubcommandConfig }

Bundle a CLI builder into a subcommand.

Subcommands use the same CLI builder that top-level CLIs do, so they are composed using the same tools. The difference lies in how subcommands are prepared for usage by parents. In addition to providing a name and a description, you also provide a mapper, which is a function that converts the subcommand's data into a common type that all subcommands under a parent command need to share. This is required since the parent command will have a field (added with the [field] function) that must have a unified type.

fooSubcommand =
    { Cli.weave <-
        foo: Opt.str { short: "f" },
        bar: Opt.str { short: "b" },
    }
    |> SubCmd.finish { name: "foobar", description: "Foo and bar subcommand", mapper: FooBar }

optional : List (SubcommandParserConfig sub_state) -> CliBuilder (Result sub_state [NoSubcommand]) GetOptionsAction GetParamsAction

Use previously defined subcommands as data in a parent CLI builder.

Once all options have been parsed, we then check the first parameter passed to see if it's one of the provided subcommands. If so, we parse the remaining arguments as that subcommand's data, and otherwise continue parsing the current command.

The optional function can only be used after all Opt fields have been registered (if any) as we don't want to parse options for a subcommand instead of a parent, and cannot be used after any parameters have been registered. This is enforced using the type state pattern, where we encode the state of the program into its types. If you're curious, check the internal Builder module to see how this works using the action type variable.

expect
    foo_subcommand =
        Opt.str { short: "f" }
        |> SubCmd.finish { name: "foo", description: "Foo subcommand", mapper: Foo }

    bar_subcommand =
        Opt.str { short: "b" }
        |> SubCmd.finish { name: "bar", description: "Bar subcommand", mapper: Bar }

    { parser } =
        SubCmd.optional [foo_subcommand, bar_subcommand],
        |> Cli.finish { name: "example" }
        |> Cli.assert_valid

    parser ["example", "bar", "-b", "abc"]
    == SuccessfullyParsed (Ok (Bar "abc"))

required : List (SubcommandParserConfig sub_data) -> CliBuilder sub_data GetOptionsAction GetParamsAction

Use previously defined subcommands as data in a parent CLI builder.

Once all options have been parsed, we then check the first parameter passed to see if it's one of the provided subcommands. If so, we parse the remaining arguments as that subcommand's data, and otherwise we fail parsing.

The required function can only be used after all Opt fields have been registered (if any) as we don't want to parse options for a subcommand instead of a parent, and cannot be used after any parameters have been registered. This is enforced using the type state pattern, where we encode the state of the program into its types. If you're curious, check the internal Builder module to see how this works using the action type variable.

expect
    foo_subcommand =
        Opt.str { short: "f" }
        |> SubCmd.finish { name: "foo", description: "Foo subcommand", mapper: Foo }

    bar_subcommand =
        Opt.str { short: "b" }
        |> SubCmd.finish { name: "bar", description: "Bar subcommand", mapper: Bar }

    { parser } =
        SubCmd.required [foo_subcommand, bar_subcommand],
        |> Cli.finish { name: "example" }
        |> Cli.assertValid

    parser ["example", "bar", "-b", "abc"]
    == SuccessfullyParsed (Bar "abc")