subcommand

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 6, 2017 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package subcommand provides a SubCommand interface and a SubCommander type that is able to execute a command line application that has multiple subcommands.

The SubCommand type provides methods needed ofr parsing and setting command line arguments and executing code once parsing is complete.

The SubCommander type is a wrapper that handles executing a SubCommand and providing help and error output in the case of errors not related to executing a SubCommand.

The help and error output follow the general form loosely based on Go templates:

{{.ErrorIfAParsingErrorNotAnExecutionError}}

{{if .KnowTheSubCommand}}
	{{.SubCommandNameAndDescriptionIfErrHelp}}

	usage: {{.SubCommander.CommandName}} {{.AvailableCommandLineArguments}}

	{{.AvailableFlagOptionUsageIfThereAreOptions}}

	{{.AvailableParameterUsageIfThereAreParameters}}
{{else}}
	{{.GlobalOptionUsageIfThereAreGlobalOptions}}

	{{.AllSubCommandUsage}}
{{end}}

Please see the examples for actual output.

Example
subCommand := &SubCommandStruct{
	NameValue: "sub1",
	ExecuteValue: func(ctx context.Context, _ io.Reader, out, _ io.Writer) error {
		fs := ctx.Value(0).(*clitest.SimpleFlagSetter)
		fmt.Fprintln(out, fs.Int)
		return nil
	},
}

globalFlags := &clitest.SimpleFlagSetter{}

sc := &SubCommander{
	GlobalFlags: globalFlags,
}
sc.Register(subCommand)

ctx := context.WithValue(context.Background(), 0, globalFlags)

err := sc.ExecuteContext(
	ctx,
	strings.Fields("-int 1234 sub1"),
	os.Stdin,
	os.Stdout,
	os.Stderr,
)
if err != nil {
	fmt.Println(err)
}
Output:
1234
Example (ErrorFlagErrHelp)
subCommand1 := &SubCommandStruct{
	NameValue:     "sub1",
	SynopsisValue: "Synopsis for sub1",
}
subCommand2 := &SubCommandStruct{
	NameValue:     "sub2",
	AliasesValue:  []string{"alias2.2", "alias2.1"},
	SynopsisValue: "Synopsis for sub2",
}

sc := &SubCommander{
	CommandName: "example_errorHelp",
	GlobalFlags: clitest.NewStringsFlagSetter("value"),
}
sc.Register(subCommand2)
sc.Register(subCommand1)

//We use os.Stdout for outErr so that we can verify output.
sc.ExecuteContext(
	context.Background(),
	strings.Fields("-h"),
	os.Stdin,
	os.Stdout,
	os.Stdout,
)
Output:
usage: example_errorHelp [global_options...] <sub_command> [[global_options | sub_command_options | parameters]...]

global_options:
  -value string
    	value_usage (default "value_default")

sub_commands:
  sub1                        Synopsis for sub1
  sub2, alias2.1, alias2.2    Synopsis for sub2
Example (ErrorParsingGlobalArguments)
subCommand1 := &SubCommandStruct{
	NameValue:     "sub1",
	SynopsisValue: "Synopsis for sub1",
}

sc := &SubCommander{
	CommandName: "example_errorParsingGlobalArguments",
	GlobalFlags: clitest.NewStringsFlagSetter("value"),
}
sc.Register(subCommand1)

//We use os.Stdout for outErr so that we can verify output.
sc.ExecuteContext(
	context.Background(),
	strings.Fields("-foo bar sub1"),
	os.Stdin,
	os.Stdout,
	os.Stdout,
)
Output:
flag provided but not defined: -foo

usage: example_errorParsingGlobalArguments [global_options...] <sub_command> [[global_options | sub_command_options | parameters]...]

global_options:
  -value string
    	value_usage (default "value_default")

sub_commands:
  sub1            Synopsis for sub1
Example (ErrorParsingSubCommandArguments)
subCommand1 := &SubCommandStruct{
	NameValue:     "sub1",
	SynopsisValue: "Synopsis for sub1",
	FlagSetter:    clitest.NewStringsFlagSetter("subflag"),
}

sc := &SubCommander{
	CommandName: "example_errorParsingSubCommandArguments",
	GlobalFlags: clitest.NewStringsFlagSetter("value"),
}
sc.Register(subCommand1)

//We use os.Stdout for outErr so that we can verify output.
sc.ExecuteContext(
	context.Background(),
	strings.Fields("-value foobar sub1 -foo bar"),
	os.Stdin,
	os.Stdout,
	os.Stdout,
)
Output:
flag provided but not defined: -foo

usage: ... sub1 [[global_options | sub_command_options]...]

global_options:
  -value string
    	value_usage (default "value_default")

sub_command_options:
  -subflag string
    	subflag_usage (default "subflag_default")
Example (ErrorUnknownSubCommand)
subCommand1 := &SubCommandStruct{
	NameValue:     "sub1",
	SynopsisValue: "Synopsis for sub1",
}

sc := &SubCommander{
	CommandName: "example_errorUnknownSubCommand",
	GlobalFlags: clitest.NewStringsFlagSetter("value"),
}
sc.Register(subCommand1)

//We use os.Stdout for outErr so that we can verify output.
sc.ExecuteContext(
	context.Background(),
	strings.Fields("-value foobar sub2"),
	os.Stdin,
	os.Stdout,
	os.Stdout,
)
Output:
unknown sub_command "sub2"

usage: example_errorUnknownSubCommand [global_options...] <sub_command> [[global_options | sub_command_options | parameters]...]

sub_commands:
  sub1            Synopsis for sub1
Example (HelpSubCommand)
subCommand1 := &SubCommandStruct{
	NameValue:        "sub1",
	SynopsisValue:    "Synopsis for sub1",
	DescriptionValue: "This is a description.",
	ParameterSetter: &clitest.ParameterSetterStruct{
		ParameterUsageValue: func() ([]*cli.Parameter, string) {
			params := []*cli.Parameter{
				&cli.Parameter{
					Name: "files",
					Many: true,
				},
			}
			return params, FormatParameter(params[0]) + " are the files to process"
		},
	},
}

sc := &SubCommander{
	CommandName: "example_helpSubCommand",
}
sc.RegisterHelp("help", "", "")
sc.Register(subCommand1)

//We use os.Stdout for outErr so that we can verify output.
sc.ExecuteContext(
	context.Background(),
	strings.Fields("help sub1"),
	os.Stdin,
	os.Stdout,
	os.Stdout,
)
Example (ListSubCommand)
subCommand1 := &SubCommandStruct{
	NameValue:     "sub1",
	SynopsisValue: "Synopsis for sub1",
}

sc := &SubCommander{
	CommandName: "example_listSubCommand",
}
sc.RegisterHelp("help", "", "", "h")
sc.RegisterList("list", "", "", "subcommands")
sc.Register(subCommand1)

//We use os.Stdout for outErr so that we can verify output.
sc.ExecuteContext(
	context.Background(),
	strings.Fields("list"),
	os.Stdin,
	os.Stdout,
	os.Stdout,
)
Output:
sub_commands:
  help, h              Prints help information for a sub_command
  list, subcommands    Prints available sub_commands
  sub1                 Synopsis for sub1

Index

Examples

Constants

View Source
const (
	Usage = cli.Usage

	GlobalOptionsName = "global_options"

	SubCommandName        = "sub_command"
	SubCommandsName       = "sub_commands"
	SubCommandOptionsName = "sub_command_options"

	ParametersName = cli.ParametersName

	ArgumentSeparator = cli.ArgumentSeparator
)

Output values that affect error and help output.

Variables

View Source
var ErrUnsuppliedSubCommand = fmt.Errorf("%s not supplied", SubCommandName)

ErrUnsuppliedSubCommand is a value error denoting a sub-command was not supplied during argument parsing. Note that this error message will not be printed to output, it is simply a sentinel value.

View Source
var FormatArgument = cli.FormatArgument

FormatArgument formats an argument's name given whether or not it is optional or multiple values are allowed.

This value may be changed to affect the output of this package.

View Source
var FormatParameter = cli.FormatParameter

FormatParameter returns a string representation of p appropriate for help and error output.

This value may be changed to affect the output of this package.

View Source
var FormatParameterName = cli.FormatParameterName

FormatParameterName returns a string representation of a Parameter name appropriate for help and error output.

This value my be changed to affect the output of this package.

Functions

func IsExecutionError

func IsExecutionError(err error) bool

IsExecutionError returns whether or not err is an ExecutingSubCommandError.

Types

type ExecutingSubCommandError

type ExecutingSubCommandError struct {
	Err error
}

ExecutingSubCommandError is an error wrapper denoting that executing a sub-command failed.

func (*ExecutingSubCommandError) Error

func (e *ExecutingSubCommandError) Error() string

Error return e.Err.Error().

type ParsingGlobalArgsError

type ParsingGlobalArgsError struct {
	Err error
}

ParsingGlobalArgsError is an error wrapper denoting global argument parsing failed.

func (*ParsingGlobalArgsError) Error

func (e *ParsingGlobalArgsError) Error() string

Error return e.Err.Error().

type ParsingSubCommandError

type ParsingSubCommandError struct {
	Err error
}

ParsingSubCommandError is an error wrapper denoting sub-command argument parsing failed.

func (*ParsingSubCommandError) Error

func (e *ParsingSubCommandError) Error() string

Error return e.Err.Error().

type SubCommand

type SubCommand interface {
	//Name returns the name of the SubCommand.
	//Names must not overlap with other SubCommand Name()s or Aliases() in the
	//same SubCommander.
	Name() string

	//Aliases returns the aliases of the SubCommand.
	//These are listed in command help output and will cause a SubCommand to execute
	//just as if the SubCommand's name were used from the command line.
	//Aliases must not overlap with other SubCommand Name()s or Aliases() in the
	//same SubCommander.
	Aliases() []string

	//Synopsis returns a synopsis of the SubCommand.
	//This should be less than one line and is used for command help output.
	Synopsis() string

	//Description returns a longer description of the SubCommand.
	//This can be however long you like and is used for help specific to this
	//SubCommand.
	Description() string

	//FlagSetter for SubCommand flags.
	//
	//This value may be called multiple times with different values for f if argument
	//parsing fails. This is done to obtain possible help and error output.
	//If there are no errors during the argument parsing process, then SetFlags
	//is only called once. Thus SetFlags should be idempotent and set static
	//values on f.
	cli.FlagSetter

	//ParameterSetter for SubCommand parameters.
	cli.ParameterSetter

	//Execute is where the SubCommand should do its work.
	//A non-nil return value indicates the execution failed and that error will
	//be processed by a SubCommander.
	//Ctx is the Command's Context provided by the calling code.
	//In, out, and outErr are the standard in, out, and err that the SubCommand
	//should use.
	Execute(ctx context.Context, in io.Reader, out, outErr io.Writer) error
}

SubCommand defines the options for a sub-command to be executed by a SubCommander.

The methods of this interface (except SetParameters and Execute) may be called multiple times to obtain help and error output when needed. These methods should be idempotent.

type SubCommandStruct

type SubCommandStruct struct {
	//NameValue is returned from SubCommand's Name() method.
	NameValue string

	//AliasesValue is returned from SubCommand's Aliases() method.
	AliasesValue []string

	//SynopsisValue is returned from SubCommand's Synopsis() method.
	SynopsisValue string

	//DescriptionValue is returned from SubCommand's Description() method.
	DescriptionValue string

	//FlagSetter is used as the SubCommand's implementation for SetFlags if not nil.
	cli.FlagSetter

	//ParameterSetter is used as the SubCommand's implementation for ParameterSetter's
	//methods if not nil.
	cli.ParameterSetter

	//ExecuteValue is used as the SubCommand's implementation if not nil.
	ExecuteValue func(context.Context, io.Reader, io.Writer, io.Writer) error
}

SubCommandStruct is a struct type implementation of SubCommand where each field represents the value to delegate to in SubCommand methods. Nil interface and func values are not used and the zero values are used for return values if not supplied.

func (*SubCommandStruct) Aliases

func (scs *SubCommandStruct) Aliases() []string

Aliases returns scs.AliasesValue.

func (*SubCommandStruct) Description

func (scs *SubCommandStruct) Description() string

Description returns scs.DescriptionValue.

func (*SubCommandStruct) Execute

func (scs *SubCommandStruct) Execute(ctx context.Context, in io.Reader, out, outErr io.Writer) error

Execute calls and returns the result from scs.ExecuteValue(ctx, out, outErr) if the field is not nil. Otherwise, it returns nil.

func (*SubCommandStruct) Name

func (scs *SubCommandStruct) Name() string

Name returns scs.NameValue.

func (*SubCommandStruct) ParameterUsage

func (scs *SubCommandStruct) ParameterUsage() ([]*cli.Parameter, string)

ParameterUsage delegates to scs.ParameterSetter if the field is not nil. Otherwise, it returns nil and the empty string.

func (*SubCommandStruct) SetFlags

func (scs *SubCommandStruct) SetFlags(f *flag.FlagSet)

SetFlags delegates to scs.FlagSetter if the field is not nil.

func (*SubCommandStruct) SetParameters

func (scs *SubCommandStruct) SetParameters(params []string) error

SetParameters delegates to scs.ParameterSetter if the field is not nil. Otherwise it returns nil.

func (*SubCommandStruct) Synopsis

func (scs *SubCommandStruct) Synopsis() string

Synopsis returns scs.SynopsisValue

type SubCommander

type SubCommander struct {
	//CommandName is used in error and help output. It should be the name of the
	//program that was invoked.
	//Usually os.Args[0].
	CommandName string

	//GlobalFlags is a FlagSetter that is used for setting global flags for subcommands.
	//
	//This value may be called multiple times with different values for f if argument
	//parsing fails. This is done to obtain possible help and error output.
	//If there are no errors during the argument parsing process, then SetFlags
	//is only called once. Thus SetFlags should be idempotent and set static
	//values on f.
	GlobalFlags cli.FlagSetter

	//DisallowGlobalFlagsWithSubCommand denotes whether or not global flags may
	//be present once the sub-command has been defined in the arguments.
	//
	//For example, a false value would allow the following arguments:
	//	["-global1" "1" "sub-command" "-global2" "2" "-subcommand1" "sc1"]
	//while true would not. A true value would require the above ["-global2" "2"]
	//to come before "sub-command" in the argument slice.
	DisallowGlobalFlagsWithSubCommand bool
	// contains filtered or unexported fields
}

SubCommander provides registering multiple SubCommands and executing them from command line arguments.

Note that SubCommander is NOT safe for use with multiple goroutines.

func (*SubCommander) Execute

func (sc *SubCommander) Execute(args []string) error

Execute is syntactic sugar for sc.ExecuteContext() with context.Background(), args, os.Stdin, os.Stdout, and os.Stderr.

func (*SubCommander) ExecuteContext

func (sc *SubCommander) ExecuteContext(ctx context.Context, args []string, in io.Reader, out, outErr io.Writer) (err error)

ExecuteContext executes a SubCommand registered with sc with the provided parameters.

Ctx is the Context passed unaltered to SubCommand.Execute.

Args should be the program arguments excluding the program name - usually os.Args[1:].

The parameters in, out, and outErr are passed unaltered to SubCommand.Execute and should represent the standard input, output, and error files for the executing SubCommand.

Err will be non-nil if parsing args failed - with type *ParsingGlobalArgsError, *ParsingSubCommandError. It will be ErrUnsuppliedSubCommand if the subcommand is not supplied in command It will be of type UnknownSubCommandError if the subcommand name arguments supplied was not found in the registered SubCommands' names or aliases. It will be of type *ExecutingSubCommandError if the SubCommand.Execute method returns an error.

If the returned error is of type *ParsingGlobalArgsError, *ParsingSubCommandError, ErrUnsuppliedSubCommand, or UnknownSubCommandError then error and help output will be written to outErr. See the package documentation for more details on error and help output. If this is the error, then execution stops and SubCommand.Execute is never called.

If the error is an *ExecutingSubCommandError then nothing is output by sc.

func (*SubCommander) Register

func (sc *SubCommander) Register(subCommand SubCommand)

Register registers subCommand to be possibly executed later via its Name() or Aliases(). This will overwrite any previously registered SubCommands with the same Name()s or Aliases().

func (*SubCommander) RegisterHelp

func (sc *SubCommander) RegisterHelp(name, synopsis, description string, aliases ...string)

RegisterHelp registers a help SubCommand that prints out help information about a required sub-command parameter. The SubCommand's name, synopsis, description, and aliases are provided as parameters. If synopsis or description are the empty string, then defaults are used.

func (*SubCommander) RegisterList

func (sc *SubCommander) RegisterList(name, synopsis, description string, aliases ...string)

RegisterList registers a list SubCommand that prints out all available sub-commands when invoked. The SubCommand's name, synopsis, description, and aliases are provided as parameters. If synopsis or description or the empty string, then defaults are used.

type UnknownSubCommandError

type UnknownSubCommandError string

UnknownSubCommandError is an error denoting the provided sub-command is not registered.

func (UnknownSubCommandError) Error

func (e UnknownSubCommandError) Error() string

Error provides the error implementation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL