Documentation
¶
Overview ¶
Package command generates (sub)commands from generic function signatures.
Index ¶
- type Command
- func MakeFixedCommand[ET ExecuteType[T], EC ExecuteMonadic[ET, T], T any](name, synopsis, usage string, executeFn EC, options ...Option) Command
- func MakeNiladicCommand(name, synopsis, usage string, executeFn ExecuteNiladicFunc, options ...Option) Command
- func MakeVariadicCommand[TS ~[]T, ET ExecuteType[TS], EC ExecuteVariadic[ET, TS, T], T any](name, synopsis, usage string, executeFn EC, options ...Option) Command
- func SubcommandGroup(name, synopsis string, subcommands []Command, options ...Option) Command
- type ExecuteArgumentsVariadicFunc
- type ExecuteDyadicFunc
- type ExecuteMonadic
- type ExecuteMonadicFunc
- type ExecuteNiladicFunc
- type ExecuteType
- type ExecuteVariadic
- type ExecuteVariadicFunc
- type FlagBinder
- type Option
- type UsageError
- type ValueNamer
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Command ¶
type Command interface { // Name returns a human friendly name of the command, // which may be used to identify commands // as well as decorate user facing help-text. Name() string // Synopsis returns a single-line short string describing the command. Synopsis() string // Usage returns an arbitrarily long string explaining how to use the command. Usage() string // Subcommands returns a list of subcommands (if any). Subcommands() []Command // Execute executes the command, with or without any arguments. Execute(ctx context.Context, args ...string) error }
Command is a decorated function ready to be executed.
func MakeFixedCommand ¶
func MakeFixedCommand[ ET ExecuteType[T], EC ExecuteMonadic[ET, T], T any, ]( name, synopsis, usage string, executeFn EC, options ...Option, ) Command
MakeFixedCommand wraps a function which accepts either ExecuteType or, ExecuteType and variadic string parameters, which are passed to `executeFn` during command.Execute.
Example ¶
MakeFixedCommand can be used to construct commands that expect a specific fixed type, and optionally, variadic arguments.
package main import ( "context" "flag" "fmt" "os" "github.com/djdv/go-filesystem-utils/internal/command" ) // fixedType is the type our execute function // expects to always receive when it's called. // The type will already be populated with default // values or values parsed from the arguments passed // to [command.Execute]. type fixedType struct { someField int } // BindFlags initializes default values for our type // and gives the [flag] package the ability to overwrite // them when parsing flags. func (ft *fixedType) BindFlags(flagSet *flag.FlagSet) { const ( flagName = "flag" flagUsage = "an example flag" flagDefault = 1 ) flagSet.IntVar(&ft.someField, flagName, flagDefault, flagUsage) } // MakeFixedCommand can be used to construct // commands that expect a specific fixed type, // and optionally, variadic arguments. func main() { var ( cmd = newFixedCommand() cmdArgs = newFixedArgsCommand() ctx = context.TODO() ) if err := cmd.Execute(ctx); err != nil { fmt.Fprint(os.Stderr, err) return } if err := cmd.Execute(ctx, "-flag=2"); err != nil { fmt.Fprint(os.Stderr, err) return } if err := cmdArgs.Execute(ctx, "-flag=3"); err != nil { fmt.Fprint(os.Stderr, err) return } if err := cmdArgs.Execute(ctx, "-flag=4", "a", "b", "c"); err != nil { fmt.Fprint(os.Stderr, err) return } } func newFixedCommand() command.Command { const ( name = "fixed" synopsis = "Prints a value." usage = "Call the command with or" + " without flags" ) return command.MakeFixedCommand[*fixedType]( name, synopsis, usage, fixedExecute, ) } func fixedExecute(ctx context.Context, settings *fixedType) error { fmt.Printf("settings.someField: %d\n", settings.someField) return nil } func newFixedArgsCommand() command.Command { const ( name = "fixed-args" synopsis = "Prints a value and arguments." usage = "Call the command with or" + " without flags or arguments" ) return command.MakeFixedCommand[*fixedType]( name, synopsis, usage, fixedExecuteArgs, ) } func fixedExecuteArgs(ctx context.Context, settings *fixedType, arguments ...string) error { if err := fixedExecute(ctx, settings); err != nil { return nil } if len(arguments) > 0 { fmt.Printf("arguments: %v\n", arguments) } return nil }
Output: settings.someField: 1 settings.someField: 2 settings.someField: 3 settings.someField: 4 arguments: [a b c]
func MakeNiladicCommand ¶
func MakeNiladicCommand( name, synopsis, usage string, executeFn ExecuteNiladicFunc, options ...Option, ) Command
MakeNiladicCommand returns a command that wraps `executeFn`.
Example ¶
MakeNiladicCommand can be used to construct basic commands that don't expect additional parameters to be passed to their execute function.
package main import ( "context" "fmt" "os" "github.com/djdv/go-filesystem-utils/internal/command" ) // MakeNiladicCommand can be used to construct // basic commands that don't expect additional // parameters to be passed to their execute function. func main() { var ( cmd = newNiladicCommand() ctx = context.TODO() ) if err := cmd.Execute(ctx); err != nil { fmt.Fprint(os.Stderr, err) return } } func newNiladicCommand() command.Command { const ( name = "niladic" synopsis = "Prints a message." usage = "Call the command with no arguments" ) return command.MakeNiladicCommand( name, synopsis, usage, niladicExecute, ) } func niladicExecute(context.Context) error { fmt.Println("hello!") return nil }
Output: hello!
func MakeVariadicCommand ¶
func MakeVariadicCommand[ TS ~[]T, ET ExecuteType[TS], EC ExecuteVariadic[ET, TS, T], T any, ]( name, synopsis, usage string, executeFn EC, options ...Option, ) Command
MakeVariadicCommand wraps a function which accepts either variaic ExecuteType or, a slice of string parameters and variadic ExecuteType which are passed to `executeFn` during command.Execute.
Example ¶
MakeVariadicCommand can be used to construct commands that expect a variable amount of parameters.
package main import ( "context" "flag" "fmt" "os" "strconv" "github.com/djdv/go-filesystem-utils/internal/command" ) type ( // settings is the type our execute function // will construct on its own. settings struct { someField int } // Execute expects to receive a list of `option`s // that it will apply to the [settings] struct. option func(*settings) error // options holds individual [option] values // and also satisfies the [command.ExecuteType] constraint. options []option ) const variadicFlagDefault = 1 // BindFlags gives the [flag] package the ability to // append options to our list during flag parsing. func (ol *options) BindFlags(flagSet *flag.FlagSet) { const ( flagName = "flag" flagUsage = "an example flag" ) flagSet.Func(flagName, flagUsage, func(parameter string) error { parsedValue, err := strconv.Atoi(parameter) if err != nil { return err } *ol = append(*ol, func(settings *settings) error { settings.someField = parsedValue return nil }) return nil }) } // MakeVariadicCommand can be used to construct // commands that expect a variable amount of parameters. func main() { var ( cmd = newVariadicCommand() cmdArgs = newVariadicArgsCommand() ctx = context.TODO() ) if err := cmd.Execute(ctx); err != nil { fmt.Fprint(os.Stderr, err) return } if err := cmd.Execute(ctx, "-flag=2"); err != nil { fmt.Fprint(os.Stderr, err) return } if err := cmdArgs.Execute(ctx, "-flag=3"); err != nil { fmt.Fprint(os.Stderr, err) return } if err := cmdArgs.Execute(ctx, "-flag=4", "a", "b", "c"); err != nil { fmt.Fprint(os.Stderr, err) return } } func newVariadicCommand() command.Command { const ( name = "variadic" synopsis = "Prints a value." usage = "Call the command with or" + " without flags" ) return command.MakeVariadicCommand[options]( name, synopsis, usage, variadicExecute, ) } func variadicExecute(ctx context.Context, options ...option) error { settings := settings{ someField: variadicFlagDefault, } for _, apply := range options { if err := apply(&settings); err != nil { return err } } fmt.Printf("settings.someField: %d\n", settings.someField) return nil } func newVariadicArgsCommand() command.Command { const ( name = "variadic-args" synopsis = "Prints a value and arguments." usage = "Call the command with or" + " without flags or arguments" ) return command.MakeVariadicCommand[options]( name, synopsis, usage, variadicExecuteArgs, ) } func variadicExecuteArgs(ctx context.Context, arguments []string, options ...option) error { if err := variadicExecute(ctx, options...); err != nil { return nil } if len(arguments) > 0 { fmt.Printf("arguments: %v\n", arguments) } return nil }
Output: settings.someField: 1 settings.someField: 2 settings.someField: 3 settings.someField: 4 arguments: [a b c]
func SubcommandGroup ¶
SubcommandGroup returns a command that only defers to subcommands. Trying to execute the command itself will return UsageError.
Example ¶
Subcommand groups can be useful for defining a section of related commands under a single named group.
package main import ( "context" "os" "github.com/djdv/go-filesystem-utils/internal/command" ) // Subcommand groups can be useful for defining // a section of related commands under a single // named group. func main() { var ( cmd = newSubcommands() ctx = context.TODO() ) // NOTE: text rendering is disabled // for `go test`'s output comparison. // Normally this can be omitted. const ( helpFlag = "-help" renderFlag = "-video-terminal=false" ) cmd.Execute(ctx, helpFlag, renderFlag) cmd.Execute(ctx, "alphabets", helpFlag, renderFlag) cmd.Execute(ctx, "numerals", helpFlag, renderFlag) } func newSubcommands() command.Command { var ( noopFn = func(context.Context) error { return nil } makeCommand = func(name string) command.Command { var ( synopsis = name + " synopsis" usage = name + " usage" ) return command.MakeNiladicCommand( name, synopsis, usage, noopFn, ) } // Printer output defaults to [os.Stderr]. // We set it here only because `go test` // compares against [os.Stdout]. output = os.Stdout cmdOptions = []command.Option{ command.WithUsageOutput(output), } ) return command.SubcommandGroup( "main", "Top level group", []command.Command{ command.SubcommandGroup( "alphabets", "Letter group.", []command.Command{ makeCommand("a"), makeCommand("b"), makeCommand("c"), }, cmdOptions..., ), command.SubcommandGroup( "numerals", "Number group.", []command.Command{ makeCommand("1"), makeCommand("2"), makeCommand("3"), }, cmdOptions..., ), }, cmdOptions..., ) }
Output: Must be called with a subcommand. Usage: main subcommand [flags] Flags: -help prints out this help text -video-terminal render text for video terminals (default: true) Subcommands: alphabets - Letter group. numerals - Number group. Must be called with a subcommand. Usage: alphabets subcommand [flags] Flags: -help prints out this help text -video-terminal render text for video terminals (default: true) Subcommands: a - a synopsis b - b synopsis c - c synopsis Must be called with a subcommand. Usage: numerals subcommand [flags] Flags: -help prints out this help text -video-terminal render text for video terminals (default: true) Subcommands: 1 - 1 synopsis 2 - 2 synopsis 3 - 3 synopsis
type ExecuteArgumentsVariadicFunc ¶
type ExecuteArgumentsVariadicFunc[ ET ExecuteType[ST], ST ~[]VT, VT any, ] interface { func(context.Context, []string, ...VT) error }
ExecuteArgumentsVariadicFunc is a variant of ExecuteVariadicFunc that also accepts arguments.
type ExecuteDyadicFunc ¶
type ExecuteDyadicFunc[ ET ExecuteType[T], T any, ] interface { func(context.Context, ET, ...string) error }
ExecuteDyadicFunc is a variant of ExecuteMonadicFunc that also accepts variadic arguments.
type ExecuteMonadic ¶
type ExecuteMonadic[ ET ExecuteType[T], T any, ] interface { ExecuteMonadicFunc[ET, T] | ExecuteDyadicFunc[ET, T] }
ExecuteMonadic permits functions with these signatures.
type ExecuteMonadicFunc ¶
type ExecuteMonadicFunc[ ET ExecuteType[T], T any, ] interface { func(context.Context, ET) error }
ExecuteMonadicFunc is a variant of ExecuteNiladicFunc that also accepts an ExecuteType.
type ExecuteNiladicFunc ¶
ExecuteNiladicFunc may be used as a command's Execute function.
type ExecuteType ¶
type ExecuteType[T any] interface { *T FlagBinder }
ExecuteType is a constraint that permits any reference type that can bind its value(s) to flags.
type ExecuteVariadic ¶
type ExecuteVariadic[ ET ExecuteType[ST], ST ~[]VT, VT any, ] interface { ExecuteVariadicFunc[ET, ST, VT] | ExecuteArgumentsVariadicFunc[ET, ST, VT] }
ExecuteVariadic permits functions with these signatures.
type ExecuteVariadicFunc ¶
type ExecuteVariadicFunc[ ET ExecuteType[ST], ST ~[]VT, VT any, ] interface { func(context.Context, ...VT) error }
ExecuteVariadicFunc is a variant of ExecuteNiladicFunc that also accepts a variadic ExecuteType.
type FlagBinder ¶
A FlagBinder should call relevant flag.FlagSet methods to bind each of it's variable references with the FlagSet. E.g. a struct would pass references of its fields to `FlagSet.Var(&structABC.fieldXYZ, ...)`.
type Option ¶
type Option func(*commandCommon)
Option is a functional option. One can be returned by the various constructors before being passed to [MakeCommand].
func WithSubcommands ¶
func WithSubcommands(subcommands ...Command) Option
WithSubcommands provides a command with subcommands. Subcommands will be called if the supercommand receives arguments that match the subcommand name.
func WithUsageOutput ¶
WithUsageOutput sets the writer that is written to when [Command.Execute] receives a request for help, or returns UsageError.
type UsageError ¶
type UsageError struct{ Err error }
UsageError may be returned by commands to signal that its usage string should be presented to the caller.
func (UsageError) Unwrap ¶
func (ue UsageError) Unwrap() error
Unwrap implements the errors.Unwrap interface.
type ValueNamer ¶
type ValueNamer interface {
Name() string
}
ValueNamer may be implemented by a flag.Value to specify the name of its parameter type, but is only used if the name is absent in the usage string.