cmd

package module
v1.3.4 Latest Latest
Warning

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

Go to latest
Published: Sep 27, 2020 License: Apache-2.0 Imports: 12 Imported by: 7

README

cmd

codecov GoDoc

Package cmd is a minimalistic library that enables easy sub commands with the standard flag library.

This library extends the standard library flag package to support sub commands and more features in a minimalistic and idiomatic API.

Features:

  • Sub commands.

  • Automatic bash completion.

  • Flag values definition and check.

  • Explicit positional arguments definition.

  • Automatic usage text.

Usage

Define a root command object using the New function. This object exposes the standard library's flag.FlagSet API, which enables adding flags in the standard way. Additionally, this object exposes the SubCommand method, which returns another command object. This objects also exposing the same API, enabling definition of flags and nested sub commands. The root object then have to be called with the Parse method, similarly to the flag.Parse call.

Principles

  • Minimalistic and flag-like.

  • Any flag that is defined in the base command will be reflected in all of its sub commands.

  • When user types the command, it starts from the command and sub commands, only then types the flags and then the positional arguments:

[command] [sub commands...] [flags...] [positional args...]
  • When a command defines positional arguments, all its sub commands has these positional arguments and thus can't define their own positional arguments.

  • When flag configuration is wrong, the program will panic.

Examples

Definition and usage of sub commands and sub commands flags.

package main

import (
	"fmt"

	"github.com/posener/cmd"
)

var (
	// Define root command with a single string flag. This object the familiar standard library
	// `*flag.FlagSet` API, so it can be used similarly.
	root  = cmd.New()
	flag0 = root.String("flag0", "", "root string flag")

	// Define a sub command from the root command with a single string flag. The sub command object
	// also have the same API as the root command object.
	sub1  = root.SubCommand("sub1", "first sub command")
	flag1 = sub1.String("flag1", "", "sub1 string flag")

	// Define a second sub command from the root command with an int flag.
	sub2  = root.SubCommand("sub2", "second sub command")
	flag2 = sub1.Int("flag2", 0, "sub2 int flag")
)

// Definition and usage of sub commands and sub commands flags.
func main() {
	// Parse command line arguments.
	root.ParseArgs("cmd", "sub1", "-flag1", "value")

	// Check which sub command was choses by the user.
	switch {
	case sub1.Parsed():
		fmt.Printf("Called sub1 with flag: %s", *flag1)
	case sub2.Parsed():
		fmt.Printf("Called sub2 with flag: %d", *flag2)
	}
}

Values

An example that shows how to use advanced configuration of flags and positional arguments using the predict package.

package main

import (
	"fmt"
	"github.com/posener/cmd"
	"github.com/posener/complete/v2/predict"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Define a flag with valid values 'foo' and 'bar', and enforce the values by `OptCheck()`.
		// The defined values will be used for bash completion, and since the OptCheck was set, the
		// flag value will be checked during the parse call.
		flag1 = root.String("flag1", "", "first flag", predict.OptValues("foo", "bar"), predict.OptCheck())
		// Define a flag to accept a valid Go file path. Choose to enforce the valid path using the
		// `OptCheck` function. The file name will also be completed in the bash completion
		// processes.
		file = root.String("file", "", "file path", predict.OptPredictor(predict.Files("*.go")), predict.OptCheck())
		// Positional arguments should be explicitly defined. Define positional arguments with valid
		// values of 'baz' and 'buzz', and choose not to enforce these values by not calling
		// `OptCheck`. These values will also be completed in the bash completion process.
		args = root.Args("[args...]", "positional arguments", predict.OptValues("baz", "buzz"))
	)

	// Parse fake command line arguments.
	root.ParseArgs("cmd", "-flag1", "foo", "-file", "cmd.go", "buz", "bazz")

	// Test:

	fmt.Println(*flag1, *file, *args)
}

Output:

foo cmd.go [buz bazz]
Args

In the cmd package, positional arguments should be explicitly defined. They are defined using the Args or ArgsVar methods.

package main

import (
	"fmt"
	"github.com/posener/cmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Positional arguments should be defined as any other flag.
		args = root.Args("[args...]", "positional arguments for command line")
	)

	// Parse fake command line arguments.
	root.ParseArgs("cmd", "v1", "v2", "v3")

	// Test:

	fmt.Println(*args)
}

Output:

[v1 v2 v3]
ArgsFn

An example of how to parse positional arguments using a custom function. It enables the advantage of using named variables such as src and dst as opposed to args[0] and args[1].

package main

import (
	"fmt"
	"github.com/posener/cmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Define variables that will hold the command line positional arguments.
		src, dst string
	)

	// Define an `ArgsFn` that converts a list of positional arguments to the named variables. It
	// should return an error when the arguments are invalid.
	argsFn := cmd.ArgsFn(func(args []string) error {
		if len(args) != 2 {
			return fmt.Errorf("expected src and dst, got %d arguments", len(args))
		}
		src, dst = args[0], args[1]
		return nil
	})

	// Should be in `init()`.
	// Register the function in the root command using the `ArgsVar` method.
	root.ArgsVar(argsFn, "[src] [dst]", "positional arguments for command line")

	// Should be in `main()`.
	root.ParseArgs("cmd", "from.txt", "to.txt")

	// Test:

	fmt.Println(src, dst)
}

Output:

from.txt to.txt
ArgsInt

An example of defining int positional arguments.

package main

import (
	"fmt"
	"github.com/posener/cmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Define a variable that will hold the positional arguments values. Use the `ArgsInt` type
		// to parse them as int.
		args cmd.ArgsInt
	)

	// Should be in `init()`.
	// Register the positional argument variable in the root command using the `ArgsVar` method.
	root.ArgsVar(&args, "[int...]", "numbers to sum")

	// Should be in `main()`.
	// Parse fake command line arguments.
	root.ParseArgs("cmd", "10", "20", "30")

	// Test:

	sum := 0
	for _, n := range args {
		sum += n
	}
	fmt.Println(sum)
}

Output:

60
ArgsN

An example of defining an exact number of positional arguments.

package main

import (
	"fmt"
	"github.com/posener/cmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Define a variable that will hold positional arguments. Create the `ArgsStr` object with
		// cap=2 to ensure that the number of arguments is exactly 2.
		args = make(cmd.ArgsStr, 2)
	)

	// Should be in `init()`.
	// Register the positional argument variable in the root command using the `ArgsVar` method
	// (similar to the Var methods of the standard library).
	root.ArgsVar(&args, "[src] [dst]", "positional arguments for command line")

	// Should be in `main()`.
	// Parse fake command line arguments.
	root.ParseArgs("cmd", "from.txt", "to.txt")

	// Test:

	fmt.Println(args)
}

Output:

[from.txt to.txt]

Readme created from Go doc with goreadme

Documentation

Overview

Package cmd is a minimalistic library that enables easy sub commands with the standard `flag` library.

This library extends the standard library `flag` package to support sub commands and more features in a minimalistic and idiomatic API.

Features:

- [x] Sub commands.

- [x] Automatic bash completion.

- [x] Flag values definition and check.

- [x] Explicit positional arguments definition.

- [x] Automatic usage text.

Usage

Define a root command object using the `New` function. This object exposes the standard library's `flag.FlagSet` API, which enables adding flags in the standard way. Additionally, this object exposes the `SubCommand` method, which returns another command object. This objects also exposing the same API, enabling definition of flags and nested sub commands. The root object then have to be called with the `Parse` method, similarly to the `flag.Parse` call.

Principles

* Minimalistic and `flag`-like.

* Any flag that is defined in the base command will be reflected in all of its sub commands.

* When user types the command, it starts from the command and sub commands, only then types the flags and then the positional arguments:

[command] [sub commands...] [flags...] [positional args...]

* When a command defines positional arguments, all its sub commands has these positional arguments and thus can't define their own positional arguments.

* When flag configuration is wrong, the program will panic.

Example

Definition and usage of sub commands and sub commands flags.

package main

import (
	"fmt"

	"github.com/posener/cmd"
)

var (
	// Define root command with a single string flag. This object the familiar standard library
	// `*flag.FlagSet` API, so it can be used similarly.
	root  = cmd.New()
	flag0 = root.String("flag0", "", "root string flag")

	// Define a sub command from the root command with a single string flag. The sub command object
	// also have the same API as the root command object.
	sub1  = root.SubCommand("sub1", "first sub command")
	flag1 = sub1.String("flag1", "", "sub1 string flag")

	// Define a second sub command from the root command with an int flag.
	sub2  = root.SubCommand("sub2", "second sub command")
	flag2 = sub1.Int("flag2", 0, "sub2 int flag")
)

// Definition and usage of sub commands and sub commands flags.
func main() {
	// Parse command line arguments.
	root.ParseArgs("cmd", "sub1", "-flag1", "value")

	// Check which sub command was choses by the user.
	switch {
	case sub1.Parsed():
		fmt.Printf("Called sub1 with flag: %s", *flag1)
	case sub2.Parsed():
		fmt.Printf("Called sub2 with flag: %d", *flag2)
	}
}
Output:

Called sub1 with flag: value
Example (Args)

In the cmd package, positional arguments should be explicitly defined. They are defined using the `Args` or `ArgsVar` methods.

package main

import (
	"fmt"

	"github.com/posener/cmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Positional arguments should be defined as any other flag.
		args = root.Args("[args...]", "positional arguments for command line")
	)

	// Parse fake command line arguments.
	root.ParseArgs("cmd", "v1", "v2", "v3")

	// Test:

	fmt.Println(*args)
}
Output:

[v1 v2 v3]
Example (ArgsFn)

An example of how to parse positional arguments using a custom function. It enables the advantage of using named variables such as `src` and `dst` as opposed to args[0] and args[1].

package main

import (
	"fmt"

	"github.com/posener/cmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Define variables that will hold the command line positional arguments.
		src, dst string
	)

	// Define an `ArgsFn` that converts a list of positional arguments to the named variables. It
	// should return an error when the arguments are invalid.
	argsFn := cmd.ArgsFn(func(args []string) error {
		if len(args) != 2 {
			return fmt.Errorf("expected src and dst, got %d arguments", len(args))
		}
		src, dst = args[0], args[1]
		return nil
	})

	// Should be in `init()`.
	// Register the function in the root command using the `ArgsVar` method.
	root.ArgsVar(argsFn, "[src] [dst]", "positional arguments for command line")

	// Should be in `main()`.
	root.ParseArgs("cmd", "from.txt", "to.txt")

	// Test:

	fmt.Println(src, dst)
}
Output:

from.txt to.txt
Example (ArgsInt)

An example of defining int positional arguments.

package main

import (
	"fmt"

	"github.com/posener/cmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Define a variable that will hold the positional arguments values. Use the `ArgsInt` type
		// to parse them as int.
		args cmd.ArgsInt
	)

	// Should be in `init()`.
	// Register the positional argument variable in the root command using the `ArgsVar` method.
	root.ArgsVar(&args, "[int...]", "numbers to sum")

	// Should be in `main()`.
	// Parse fake command line arguments.
	root.ParseArgs("cmd", "10", "20", "30")

	// Test:

	sum := 0
	for _, n := range args {
		sum += n
	}
	fmt.Println(sum)
}
Output:

60
Example (ArgsN)

An example of defining an exact number of positional arguments.

package main

import (
	"fmt"

	"github.com/posener/cmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Define a variable that will hold positional arguments. Create the `ArgsStr` object with
		// cap=2 to ensure that the number of arguments is exactly 2.
		args = make(cmd.ArgsStr, 2)
	)

	// Should be in `init()`.
	// Register the positional argument variable in the root command using the `ArgsVar` method
	// (similar to the Var methods of the standard library).
	root.ArgsVar(&args, "[src] [dst]", "positional arguments for command line")

	// Should be in `main()`.
	// Parse fake command line arguments.
	root.ParseArgs("cmd", "from.txt", "to.txt")

	// Test:

	fmt.Println(args)
}
Output:

[from.txt to.txt]
Example (Values)

An example that shows how to use advanced configuration of flags and positional arguments using the predict package.

package main

import (
	"fmt"

	"github.com/posener/cmd"
	"github.com/posener/complete/v2/predict"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = cmd.New()
		// Define a flag with valid values 'foo' and 'bar', and enforce the values by `OptCheck()`.
		// The defined values will be used for bash completion, and since the OptCheck was set, the
		// flag value will be checked during the parse call.
		flag1 = root.String("flag1", "", "first flag", predict.OptValues("foo", "bar"), predict.OptCheck())
		// Define a flag to accept a valid Go file path. Choose to enforce the valid path using the
		// `OptCheck` function. The file name will also be completed in the bash completion
		// processes.
		file = root.String("file", "", "file path", predict.OptPredictor(predict.Files("*.go")), predict.OptCheck())
		// Positional arguments should be explicitly defined. Define positional arguments with valid
		// values of 'baz' and 'buzz', and choose not to enforce these values by not calling
		// `OptCheck`. These values will also be completed in the bash completion process.
		args = root.Args("[args...]", "positional arguments", predict.OptValues("baz", "buzz"))
	)

	// Parse fake command line arguments.
	root.ParseArgs("cmd", "-flag1", "foo", "-file", "cmd.go", "buz", "bazz")

	// Test:

	fmt.Println(*flag1, *file, *args)
}
Output:

foo cmd.go [buz bazz]

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func OptDetails

func OptDetails(details string) optionFn

OptDetails sets a detailed description to the root command.

func OptErrorHandling

func OptErrorHandling(errorHandling flag.ErrorHandling) optionRootFn

OptErrorHandling defines the behavior in case of an error in the `Parse` function.

func OptName

func OptName(name string) optionRootFn

OptName sets a predefined name to the root command.

func OptOutput

func OptOutput(w io.Writer) optionRootFn

OptOutput sets the output for the usage.

func OptSynopsis

func OptSynopsis(synopsis string) optionRootFn

OptSynopsis sets a description to the root command.

Types

type ArgsFn

type ArgsFn func([]string) error

ArgsFn is a function that implements Args. Usage example:

var (
	root     = cmd.Root()
	src, dst string
)

func setArgs(args []string) error {
	if len(args) != 2 {
		return fmt.Errorf("expected src and dst, got %d arguments", len(args))
	}
	src, dst = args[0], args[1]
	return nil
}

func init() {
	root.ArgsVar(cmd.ArgsFn(setArgs), "[src] [dst]", "define source and destination")
}

func (ArgsFn) Set

func (f ArgsFn) Set(args []string) error

Set implements the ArgsValue interface.

type ArgsInt

type ArgsInt []int

ArgsInt are int positional arguments. If it is created with cap > 0, it will be used to define the number of required arguments.

Usage

To get a list of arbitrary number of integers:

root := cmd.Root()

var cmd.ArgsInt args
root.ArgsVar(&args, "[int...]", "list of integer args")

To get a list of specific number of integers:

root := cmd.Root()

args := make(cmd.ArgsInt, 3)
root.ArgsVar(&args, "[int1] [int2] [int3]", "list of 3 integers")

func (*ArgsInt) Set

func (a *ArgsInt) Set(args []string) error

Set implements the ArgsValue interface.

type ArgsStr

type ArgsStr []string

ArgsStr are string positional arguments. If it is created with cap > 0, it will be used to define the number of required arguments.

Usage

To get a list of arbitrary number of arguments:

root := cmd.Root()

var cmd.ArgsStr args
root.ArgsVar(&args, "[arg...]", "list of arguments")

To get a list of specific number of arguments:

root := cmd.Root()

args := make(cmd.ArgsStr, 3)
root.ArgsVar(&args, "[arg1] [arg2] [arg3]", "list of 3 arguments")

func (*ArgsStr) Set

func (a *ArgsStr) Set(args []string) error

Set implements the ArgsValue interface.

type ArgsValue

type ArgsValue interface {
	// Set should assign values to the positional arguments variable from list of positional
	// arguments from the command line. It should return an error if the given list does not fit
	// the requirements.
	Set([]string) error
}

ArgsValue is interface for positional arguments variable. It can be used with the `(*Cmd).ArgsVar` method. For examples of objects that implement this interface see ./args.go.

type Cmd

type Cmd struct {
	*SubCmd
}

Cmd is a command that can have set of flags and sub commands.

func New

func New(options ...optionRoot) *Cmd

New creates a new root command.

func (*Cmd) Parse

func (c *Cmd) Parse() error

Parse command line arguments.

func (*Cmd) ParseArgs

func (c *Cmd) ParseArgs(args ...string) error

ParseArgs a set of arguments.

type SubCmd

type SubCmd struct {

	// flagsSet holds the flags of the command.
	*compflag.FlagSet
	// contains filtered or unexported fields
}

SubCmd is a sub command that can have a set of flags and sub commands.

func (*SubCmd) Args

func (c *SubCmd) Args(usage, details string, options ...predict.Option) *[]string

Args returns the positional arguments for the command and enable defining options. Only a sub command that called this method accepts positional arguments. Calling a sub command with positional arguments where they were not defined result in parsing error. The provided options can be nil for default values.

func (*SubCmd) ArgsVar

func (c *SubCmd) ArgsVar(value ArgsValue, usage, details string, options ...predict.Option)

ArgsVar should be used to parse arguments with specific requirements or to specific object/s. For example, accept only 3 positional arguments:

var (
	root = cmd.Root()
	args = make(cmd.ArgsStr, 3)
)

func init() {
	root.ArgsVar(args, "[arg1] [arg2] [arg3]", "provide 3 positional arguments")
}

The value argument can optionally implement `github.com/posener/complete.Predictor` interface. Then, command completion for the predictor will apply.

func (*SubCmd) SubCommand

func (c *SubCmd) SubCommand(name string, synopsis string, options ...option) *SubCmd

SubCommand creates a new sub command to the given command.

func (*SubCmd) Usage

func (c *SubCmd) Usage()

Usage prints the sub command usage to the defined output.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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