clicommand

package module
v0.0.0-...-b81063b Latest Latest
Warning

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

Go to latest
Published: May 27, 2018 License: BSD-2-Clause Imports: 3 Imported by: 0

README

go-clicommand

clicommand provides Go CLI applications with subcommand/api-style interfaces and option/parameter handling

GoDoc Go Report Card Build Status

Installation

go get -u github.com/leehuk/go-clicommand

Overview

The clicommand library makes the creation of Go CLI applications using a subcommand interface easier. The subcommand interface is structured as a parent/child tree so the application can mimic an api, with edges of the tree running custom Handler functions and the tree providing a structured way of grouping commands, attaching option arguments and finding additional parameters.

Command Tree

Command objects are chained together to build a tree which has arbitrary depth, providing it follows the tree rules:

  • Each parent command within the tree may have any number of children.
  • Each child command object within the tree has a single parent.
  • Every child Command object within the tree can have its own children, except when it has a Handler function.

CLI Application Pseudo Example

This allows building a CLI application which can mimic an API, e.g.:

./clicommand                         // parent, has children
./clicommand http                    // child of clicommand, has children itself
./clicommand http get => Handler()   // child of clicommand->http, calls Handler() when run.
                                     // Cannot have children.
./clicommand http post => Handler()  // child of clicommand->http, calls Handler() when run.
                                     // Cannot have children.

CLI Options

Options can be attached to the tree at any point and these are inherited along the tree, so child commands also have options from their parent commands. Options are defined as either having or not having parameters, options with parameters use double dashes and options without parameters use single dashes as selectors.

CLI Parameters

Anything the parser doesnt recognise is stored as a parameter, alloowing applications to accept things as a simple generic parameter, rather than requiring its specified as an option.

Autogenerated Help

As each command and option is added to the tree with a name and description, the parser can automatically construct help information and display it when the program is run without parameters, or the 'help' command is used. The following example uses the sample helloworld program from https://git.io/vNDug

[golang@908222b2e8aa helloworld]$ ./helloworld help

helloworld - Sample hello world program
helloworld [-u] [-lower]

helloworld options:
   -u                    Uppercase output
   -lower                Lowercase output

Available subcommands:
  hello        Says hello world
  say          Says something

For help information run:
  'helloworld help' .. 'helloworld <commands>* help' .. 'helloworld [commands]* help [subcommand]*'

[golang@908222b2e8aa helloworld]$ 

Sample Program

A sample helloworld.go program can be found under examples.

Documentation

Overview

Package clicommand provides CLI applications with subcommand/api-style interfaces and option/parameter handling

The clicommand library makes the creation of Go CLI applications using a subcommand interface easier. The subcommand interface is structured as a parent/child tree so the application can mimic an api, with edges of the tree running custom Handler functions and the tree providing a structured way of grouping commands, attaching option arguments and finding additional parameters.

Command Tree

Command objects are chained together to build a tree which has arbitrary depth, providing it follows the tree rules:

Each parent Command object within the tree may have any number of children. Each child Command object within the tree has a single parent. Every child Command object within the tree can have its own children, except when it has a Handler function.

CLI Application Pseudo Example

This allows building a CLI application which can mimic an API, e.g.:

./clicommand                         // parent, has children
./clicommand http                    // child of clicommand, has children itself
./clicommand http get => Handler()   // child of clicommand->http, calls Handler() when run.
                                     // Cannot have children.
./clicommand http post => Handler()  // child of clicommand->http, calls Handler() when run.
                                     // Cannot have children.

CLI Options

Options can be attached to the tree at any point and these are inherited along the tree, so child commands also have options from their parent commands. Options are defined as either having or not having parameters, options with parameters use double dashes and options without parameters use single dashes as selectors.

CLI Parameters

Anything the parser doesnt recognise is stored as a parameter, alloowing applications to accept things as a simple parameter, rather than requiring its specified as an option.

Autogenerated Help

As each command and option is added to the tree with a name and description, the parser can automatically construct help information and display it when the program is run without parameters, or the 'help' command is used. The following example uses the sample helloworld program from https://git.io/vNDug

[golang@908222b2e8aa helloworld]$ ./helloworld help

helloworld - Sample hello world program
helloworld [-u] [-lower]

helloworld options:
   -u                    Uppercase output
   -lower                Lowercase output

Available subcommands:
  hello        Says hello world
  say          Says something

For help information run:
  'helloworld help' .. 'helloworld <commands>* help' .. 'helloworld [commands]* help [subcommand]*'

Sample Program

This is a sample program built using this module.

Example

This is an example hello world program built using https://github.com/leehuk/go-clicommand It creates a program with a subcommand "hello", which has two subcommands "world" and "say". Both commands have shared options "-u" to uppercase the output, and "-lower" to lowercase the output.

package main

/*
This program could be called with the following examples:

	./command hello world
		Outputs: "Hello, world!"

	./command hello -u world
		Outputs: "HELLO, WORLD!"

	./command hello world -lower
		Outputs : "hello, world!"

	./command hello say -u --say "This is a test"
		Outputs: "THIS IS A TEST"

	./command
	./command help
	./command hello help
	./command hello world help
		Outputs: Autogenerated help information

	./command hello
		Returns error as no subcommand specified.

	./command hello say
		Returns error as "--say" option not specified.

	./command -u hello world
		Returns error as "-u" option is not in global scope
*/

import (
	"fmt"
	"os"
	"strings"

	"github.com/leehuk/go-clicommand"
)

// getTextWithOptions is a simple helper function, that uses data.Options["u"]
// as a control to uppercase the string, data.Options["lower"] as a control
// to lowercase the string, or otherwise returns the string as-is
func getTextWithOptions(text string, data *clicommand.Data) string {
	if _, ok := data.Options["u"]; ok {
		return strings.ToUpper(text)
	} else if _, ok := data.Options["lower"]; ok {
		return strings.ToLower(text)
	}

	return text
}

// sayHelloWorld Handler function for "hello" -> "world" command.  Says
// "Hello, world!"
func sayHelloWorld(data *clicommand.Data) error {
	var text = getTextWithOptions("Hello, world!", data)
	fmt.Printf("%s\n", text)
	return nil
}

// sayHelloSomething Handler function for "hello" -> "say" command.  Says
// whatever is specified in data.Options["say"]
func sayHelloSomething(data *clicommand.Data) error {
	var text = getTextWithOptions(data.Options["say"], data)
	fmt.Printf("%s\n", text)
	return nil
}

// This is an example hello world program built using https://github.com/leehuk/go-clicommand
// It creates a program with a subcommand "hello", which has two subcommands "world"
// and "say".  Both commands have shared options "-u" to uppercase the output, and
// "-lower" to lowercase the output.
func main() {
	// Our root command for the tree.  handler is specified as nil, as this
	// will have children.
	cliRoot := clicommand.NewCommand("hw", "Sample hello world program", nil)

	// Create a hello command off our root object
	cliHello := cliRoot.NewCommand("hello", "Hello saying options", nil)

	// These options are available to all subcommands of hello
	cliHello.NewOption("u", "Uppercase output", false)
	cliHello.NewOption("lower", "Lowercase output", false)

	// Create child of hello, world
	cliHello.NewCommand("world", "Says hello world", sayHelloWorld)

	// Create child command of hello, say
	cliHelloSomething := cliHello.NewCommand("say", "Says something", sayHelloSomething)
	// Create a required option to hello say, with a parameter
	cliHelloSomething.NewOption("say", "Thing to say", true).SetRequired()

	if error := cliRoot.Parse(); error != nil {
		fmt.Printf("%v\n", error)
		os.Exit(1)
	}
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Command

type Command struct {
	// Name Name of subcommand
	Name string
	// Desc Description of subcommand
	Desc string
	// Handler Handler function subcommand calls, nil for subcommands with children
	Handler Handler
	// Parent Command object thats the parent of this one
	Parent *Command
	// Children Command objects that are children of this one
	Children []*Command
	// Options Option arguments
	Options []*Option
	// Callbackspre Callbacks to run pre-verification
	Callbackspre []Handler
	// Callbacks Callbacks to run as part of verification
	Callbacks []Handler
}

A Command represents a command of the cli program. These are chained into a tree with links in both child and parent directions

Each child can itself be a parent, but only leaf nodes (children with no children) can have handler functions, e.g.

clicommand -->
  clicommand api -->
    clicommand api get ==> handler
    clicommand api delete ==> handler

func NewCommand

func NewCommand(name string, desc string, handler Handler) *Command

NewCommand creates a new command, unbound to parents. This is generally only used for creating the root object as after that, the func (c *Command) NewCommand() variant is easier, as it automatically binds the child Command.

handler must be nil if this command will have its own children.

func (*Command) BindCallback

func (c *Command) BindCallback(handler Handler)

BindCallback binds a validation callback, that can be used to add extra validation around commands and options.

Callbacks are processed starting at the leaf, moving up to the root. Only callbacks directly along that path are executed.

func (*Command) BindCallbackPre

func (c *Command) BindCallbackPre(handler Handler)

BindCallbackPre binds a pre-validation callback, that can be used to alter the user-provided options and Command tree prior to validation. This can be useful for things like translating environment variables into options, or making options required/not-required for certain commands.

Callbacks are processed starting at the leaf, moving up to the root. Only callbacks directly on that path are executed.

func (*Command) BindCommand

func (c *Command) BindCommand(cmdv ...*Command)

BindCommand binds a series of subcommands as children. Links are placed in both directions, from parent -> child and child -> parent.

If the parent already has a handler set, this will panic.

func (*Command) BindOption

func (c *Command) BindOption(optionv ...*Option)

BindOption binds an Option as a child.

func (*Command) GetCommand

func (c *Command) GetCommand(name string) *Command

GetCommand finds a child Command with the given name, or nil if not found. name matches are case-insensitive.

func (*Command) GetNameChain

func (c *Command) GetNameChain() string

GetNameChain builds a space separated string of all Command names from itself up to the root.

func (*Command) GetNameTop

func (c *Command) GetNameTop() string

GetNameTop finds the name of the root Command.

func (*Command) GetOption

func (c *Command) GetOption(name string, param bool) *Option

GetOption finds an child Option with the given name and the same parameter, searching the entire way up the tree to the root if necessary.

func (*Command) NewCommand

func (c *Command) NewCommand(name string, desc string, handler Handler) *Command

NewCommand creates a new Command and automatically binds it as a child.

If the new Command will also have its own children, handler must be set to nil. If the parent already has a handler set, this will panic.

func (*Command) NewOption

func (c *Command) NewOption(name string, desc string, param bool) *Option

NewOption creates a new Option and automatically binds it as a child.

func (*Command) Parse

func (c *Command) Parse() error

Parse parses the command line from os.Args under the supplied command tree, then acts accordingly based on the results.

Everything specified on the command line is either a subcommand, option or a generic parameter.

Once parsing is complete, pre callbacks are made, then we either proceed to display internal help information if requested, or we perform internal verification, then call the validation callbacks, then finally if everything is ok call the wanted Handler.

The parsing will steal the arg "help" if it detects it as the first unknown parameter, allowing for easy access to the available commands and options.

If parsing is not ok, it will return one of several internal error types.

func (*Command) UnbindOption

func (c *Command) UnbindOption(optionv ...*Option)

UnbindOption unbinds an Option so it is no longer a child.

type Data

type Data struct {
	// Cmd is a pointer to the Command object which has triggered the Handler
	// to be called.
	//
	// If the Handler is a registered callback, the pointer is to the Command
	// object that is about to be executed.
	Cmd *Command

	// Options is a map of options supplied to the command.  The key is the
	// option selected by the user, with the value being the parameter supplied
	// to that option.
	//
	// For Option objects which do not take parameters, the value is an empty
	// string.
	//
	// E.g.:
	//   ./clicommand ... --foo bar ... -q ...
	// Becomes:
	//   Options["foo"] = "bar"
	//   Options["q"] = ""
	//
	// Callbacks may modify the Options directly, the parser then sees these
	// as if they were directly supplied by the user.  Callbacks of this type
	// should generally be bound via BindCallbackPre(), allowing verification
	// to be performed as normal within standard callbacks.
	Options map[string]string

	// Params is an array of additional parameters the parser did not recognise.
	// Effectively, when the parser finds a non-option argument which doesnt
	// match any more commands, the remaining non-option fields become
	// parameters.
	Params []string
}

The Data structure is passed to all Handler functions called as a result of a given Command being run. This structure is also passed to any registered callbacks during the parsing stage.

type ErrCallback

type ErrCallback struct {
	// contains filtered or unexported fields
}

ErrCallback Error type for when a callback has failed.

func (*ErrCallback) Error

func (e *ErrCallback) Error() string

type ErrCallbackPre

type ErrCallbackPre struct {
	// contains filtered or unexported fields
}

ErrCallbackPre Error type for when a pre-validation callback has failed.

func (*ErrCallbackPre) Error

func (e *ErrCallbackPre) Error() string

type ErrCommandError

type ErrCommandError struct {
	// contains filtered or unexported fields
}

ErrCommandError Error type for when a command has returned an error.

func (*ErrCommandError) Error

func (e *ErrCommandError) Error() string

type ErrCommandInvalid

type ErrCommandInvalid struct {
	// contains filtered or unexported fields
}

ErrCommandInvalid Error type for when the command line uses a subcommand that does not exist.

func (*ErrCommandInvalid) Error

func (e *ErrCommandInvalid) Error() string

type ErrCommandMissing

type ErrCommandMissing struct{}

ErrCommandMissing Error type for when the command line has ended with a parent command with no handler, meaning one of its children needed to be chosen instead.

func (*ErrCommandMissing) Error

func (e *ErrCommandMissing) Error() string

type ErrOptionMissing

type ErrOptionMissing struct {
	// contains filtered or unexported fields
}

ErrOptionMissing Error type for when a required option is missing.

func (*ErrOptionMissing) Error

func (e *ErrOptionMissing) Error() string

type ErrOptionMissingParam

type ErrOptionMissingParam struct {
	// contains filtered or unexported fields
}

ErrOptionMissingParam Error type for when the command line contains an option that requires a parameter, but one is not specified

func (*ErrOptionMissingParam) Error

func (e *ErrOptionMissingParam) Error() string

type ErrOptionUnknown

type ErrOptionUnknown struct {
	// contains filtered or unexported fields
}

ErrOptionUnknown Error type for when the command line contains an option, that is not defined in the command tree.

func (*ErrOptionUnknown) Error

func (e *ErrOptionUnknown) Error() string

type Handler

type Handler func(*Data) (err error)

A Handler represents a function to be called when a particular Command is selected, or a callback is required. The Handler function must be defined as:

func myHandlerFunc(data *Data) error {
  // your code goes here
}

The Data parameter contains a pointer to the selected Command, a map of supplied options and an array of the supplied parameters.

If the Handler function encounters an error it should return this as an error and it will automatically be sent to stderr. The Handler function should return nil on success.

type Option

type Option struct {
	// name Name of option, stored without its dashes prefix.
	Name string

	// desc Description of option
	Desc string

	// param Controls whether this option takes parameters or not.
	//
	// For simplicity, all options that take parameters have a double dash
	// prefix, whilst options without parameters have a single dash prefix.
	// E.g.
	//   --option1 <required parameter>
	//   -option2 // no paramater
	Param bool

	// required Controls whether this option must be supplied or not.
	// If a parameter is marked as required, the parser will automatically
	// detect it is not supplied and return an error.
	Required bool

	// parents Array of pointers which this Option is bound to
	Parents []*Command
}

An Option represents a defined option parameter that can be specified on the command line when the program is run.

Options can be attached to multiple Command objects.

Options either have, or do not have parameters and the parser detects the difference by whether they are specified with a double dash prefix or a single dash prefix. Options with a double dash prefix (e.g. --foo) will have the next field of the command string taken as its parameter.

The parser will always perform case-insensitive matches on option names, but will also match the parameter type. If a non-parameter (e.g. -bar) is specified, but the Option has been added as a parameter type (e.g. --bar) the parser will treat it as an unknown option.

func NewOption

func NewOption(name string, desc string, param bool) *Option

NewOption creates a new Option object with the given name and desc, but does not bind it within the tree. This is generally useful when creating a generic Option, which needs to be bound to multiple Command objects.

The param field is used to determine whether this option takes an additional parameter after it.

func (*Option) BindCommand

func (o *Option) BindCommand(cmd *Command)

BindCommand binds an Option to the given Command object, so it is available to be specified for that command, and all child commands.

func (*Option) GetParents

func (o *Option) GetParents() []*Command

GetParents returns the parents Command objects of an Option

func (*Option) GetRequired

func (o *Option) GetRequired() bool

GetRequired returns whether this Option must be specified. This requirement only applies to Options that are directly on the path between the edge Command and the root.

func (*Option) SetRequired

func (o *Option) SetRequired() *Option

SetRequired marks the Option so it must be specified. This requirement only applies to Options that are directly on the path between the edge Command and the root.

func (*Option) UnbindCommand

func (o *Option) UnbindCommand(cmd *Command)

UnbindCommand unbinds an Option from the given Command object, at which point it is no longer available for that command or its children.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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