command

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: May 23, 2023 License: BSD-3-Clause Imports: 12 Imported by: 24

README

command

GoDoc

This repository provides Go package that implements basic subcommand dispatch for Go command-line programs. The design of this package is based on the way the "go" command-line tool works, but the implementation is not shared.

This package is in development, the API is likely to change in various ways.

Documentation

Overview

Package command defines plumbing for command dispatch. It is based on and similar in design to the "go" command-line tool.

Overview

The command package allows a program to easily process a simple language of named commands, each of which may have its own flags, arguments, and nested subcommands. A command is represented by a *command.C value carrying help text, usage summaries, and a function to execute its behavior.

The Run and RunOrFail functions parse the raw argument list of a program against a tree of *command.C values, parsing flags as needed and executing the selected command or printing appropriate diagnostics. Flags are parsed using the standard "flag" package by default.

Example
package main

import (
	"flag"
	"fmt"
	"strings"

	"github.com/creachadair/command"
)

func main() {
	// The environment passed to a command can carry an arbitrary config value.
	// Here we use a struct carrying information about options.
	type options struct {
		noNewline bool
	}

	root := &command.C{
		Name: "example",

		// Usage may have multiple lines, and can omit the command name.
		Usage: "command args...",

		// The first line of the help text is used as "short" help.
		// Any subsequent lines are include in "long" help.
		Help: `Do interesting things with arguments.

This program demonstrates the use of the command package.
This help text is printed by the "help" subcommand.`,

		// Note that the "example" command does not have a Run function.
		// Executing it without a subcommand will print a help message and exit
		// with error.

		Commands: []*command.C{
			// This function creates a basic "help" command that prints out
			// command help based on usage and help strings.
			//
			// This command can also have "help topics", which are stripped-down
			// commands with only help text (no flags, usage, or other behavior).
			command.HelpCommand([]command.HelpTopic{{
				Name: "special",
				Help: "This is some useful information a user might care about.",
			}, {
				Name: "magic",
				Help: `The user can write "command help <topic>" to get this text.`,
			}}),

			// This is a typical user-defined command.
			{
				Name:  "echo",
				Usage: "text ...",
				Help:  "Concatenate the arguments with spaces and print to stdout.",

				SetFlags: func(env *command.Env, fs *flag.FlagSet) {
					// Pull the config value out of the environment and attach a flag to it.
					opt := env.Config.(*options)
					fs.BoolVar(&opt.noNewline, "n", false, "Do not print a trailing newline")
				},

				Run: func(env *command.Env) error {
					opt := env.Config.(*options)
					fmt.Print(strings.Join(env.Args, " "))
					if !opt.noNewline {
						fmt.Println()
					}
					return nil
				},
			},
		},
	}

	// Demonstrate help output.
	//
	// Note that the argument to NewEnv is plumbed via the Config field of Env.
	opt := new(options)
	command.Run(root.NewEnv(opt), []string{"help"})

	command.RunOrFail(root.NewEnv(opt), []string{"echo", "foo", "bar"})
	command.RunOrFail(root.NewEnv(opt), []string{"echo", "-n", "baz"})
}
Output:

foo bar
baz

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrRequestHelp = errors.New("help requested")

ErrRequestHelp is returned from Run if the user requested help.

Functions

func FailWithUsage

func FailWithUsage(env *Env) error

FailWithUsage is a run function that logs a usage message for the command and returns ErrRequestHelp.

func Run

func Run(env *Env, rawArgs []string) error

Run traverses the given unprocessed arguments starting from env. See the documentation for type C for a description of argument traversal.

Run writes usage information to ctx and returns a UsageError if the command-line usage was incorrect or ErrRequestHelp if the user requested help via the --help flag.

func RunHelp

func RunHelp(env *Env) error

RunHelp is a run function that implements long help. It displays the help for the enclosing command or subtopics of "help" itself.

func RunOrFail

func RunOrFail(env *Env, rawArgs []string)

RunOrFail behaves as Run, but prints a log message and calls os.Exit if the command reports an error. If the command succeeds, RunOrFail returns.

If a command reports a UsageError or ErrRequestHelp, the exit code is 2. For any other error the exit code is 1.

Types

type C

type C struct {
	// The name of the command, preferably one word. The name is during argument
	// processing to choose which command or subcommand to execute.
	Name string

	// A terse usage summary for the command. Multiple lines are allowed.
	// Each line should be self-contained for a particular usage sense.
	Usage string

	// A detailed description of the command. Multiple lines are allowed.
	// The first non-blank line of this text is used as a synopsis; the whole
	// string is printed for long help.
	Help string

	// Flags parsed from the raw argument list. This will be initialized before
	// Init or Run is called unless CustomFlags is true.
	Flags flag.FlagSet

	// If false, Flags is used to parse the argument list.  Otherwise, the Run
	// function is responsible for parsing flags from the argument list.
	CustomFlags bool

	// Perform the action of the command. If nil, calls FailWithUsage.
	Run func(env *Env) error

	// If set, this will be called before flags are parsed, to give the command
	// an opportunity to set flags.
	SetFlags func(env *Env, fs *flag.FlagSet)

	// If set, this will be called after flags are parsed (if any) but before
	// any subcommands are processed. If it reports an error, execution stops
	// and that error is returned to the caller.
	//
	// The Init callback is permitted to modify env, and any such modifications
	// will persist through the rest of the invocation.
	Init func(env *Env) error

	// Subcommands of this command.
	Commands []*C
	// contains filtered or unexported fields
}

C carries the description and invocation function for a command.

To process a command-line, the Run function walks through the arguments starting from a root command to discover which command should be run and what flags it requires. This argument traversal proceeds in phases:

When a command is first discovered during argument traversal, its SetFlags hook is executed (if defined) to prepare its flag set. Then, unless the CustomFlags option is true, the rest of the argument list is parsed using the FlagSet to separate command-specific flags from further arguments and/or subcommands.

After flags are parsed and before attempting to explore subcommands, the current command's Init hook is called (if defined). If Init reports an error it terminates argument traversal, and that error is reported back to the user.

Next, if there are any remaining non-flag arguments, Run checks whether the current command has a subcommand matching the first argument. If so argument traversal recurs into that subcommand to process the rest of the command-line.

Otherwise, if the command defines a Run hook, that hook is executed with the remaining unconsumed arguments. If no Run hook is defined, the traversal stops, logs a help message, and reports an error.

func HelpCommand

func HelpCommand(topics []HelpTopic) *C

HelpCommand constructs a standardized help command with optional topics. The caller is free to edit the resulting command, each call returns a separate value.

func VersionCommand

func VersionCommand() *C

VersionCommand constructs a standardized version command that prints version metadata from the running binary to stdout. The caller can safely modify the returned command to customize its behavior.

func (*C) FindSubcommand

func (c *C) FindSubcommand(name string) *C

FindSubcommand returns the subcommand of c matching name, or nil.

func (*C) HasRunnableSubcommands

func (c *C) HasRunnableSubcommands() bool

HasRunnableSubcommands reports whether c has any runnable subcommands.

func (*C) HelpInfo

func (c *C) HelpInfo(includeCommands bool) HelpInfo

HelpInfo returns help details for c. If includeCommands is true and c has subcommands, their help is also generated.

A command or subcommand with no Run function and no subcommands of its own is considered a help topic, and listed separately.

func (*C) NewEnv

func (c *C) NewEnv(config any) *Env

NewEnv returns a new root context for c with the optional config value.

func (*C) Runnable

func (c *C) Runnable() bool

Runnable reports whether the command has any action defined.

type Env

type Env struct {
	Parent  *Env      // if this is a subcommand, its parent environment (or nil)
	Command *C        // the C value that carries the Run function
	Config  any       // configuration data
	Args    []string  // the unclaimed command-line arguments
	Log     io.Writer // where to write diagnostic output (nil for os.Stderr)
	// contains filtered or unexported fields
}

Env is the environment passed to the Run function of a command. An Env implements the io.Writer interface, and should be used as the target of any diagnostic output the command wishes to emit. Primary command output should be sent to stdout.

func (*Env) Cancel added in v0.0.2

func (e *Env) Cancel(cause error)

Cancel cancels the context associated with e with the given cause. If e does not have its own context, the cancellation is propagated to its parent if one exists. If e has no parent and no context, Cancel does nothing without error.

func (*Env) Context added in v0.0.2

func (e *Env) Context() context.Context

Context returns the context associated with e. If e does not have its own context, it returns the context of its parent, or if e has no parent it returns a new background context.

func (*Env) SetContext added in v0.0.2

func (e *Env) SetContext(ctx context.Context) *Env

SetContext sets the context of e to ctx and returns e. If ctx == nil it clears the context of e so that it efaults to its parent (see Context).

func (*Env) Usagef

func (e *Env) Usagef(msg string, args ...any) error

Usagef returns a formatted error that describes a usage error for the command whose environment is e. The result has concrete type UsageError.

func (*Env) Write

func (e *Env) Write(data []byte) (int, error)

Write implements the io.Writer interface. Writing to a context writes to its designated output stream, allowing the context to be sent diagnostic output.

type HelpInfo

type HelpInfo struct {
	Name     string
	Synopsis string
	Usage    string
	Help     string
	Flags    string

	// Help for subcommands (populated if requested)
	Commands []HelpInfo

	// Help for subtopics (populated if requested)
	Topics []HelpInfo
}

HelpInfo records synthesized help details for a command.

func (HelpInfo) WriteLong

func (h HelpInfo) WriteLong(w io.Writer)

WriteLong writes a complete help description to w, including a usage summary, full help text, flag summary, and subcommands.

func (HelpInfo) WriteSynopsis

func (h HelpInfo) WriteSynopsis(w io.Writer)

WriteSynopsis writes a usage summary and command synopsis to w. If the command defines flags, the flag summary is also written.

func (HelpInfo) WriteUsage

func (h HelpInfo) WriteUsage(w io.Writer)

WriteUsage writes a usage summary to w.

type HelpTopic

type HelpTopic struct {
	Name string
	Help string
}

A HelpTopic specifies a name and some help text for use in constructing help topic commands.

type UsageError

type UsageError struct {
	Env     *Env
	Message string
}

UsageError is the concrete type of errors reported by the Usagef function, indicating an error in the usage of a command.

func (UsageError) Error

func (u UsageError) Error() string

Jump to

Keyboard shortcuts

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