subcmd

package module
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2019 License: Apache-2.0 Imports: 9 Imported by: 0

README

subcmd

Build Status codecov GoDoc goreadme

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

Define a root command object using the Root 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 or ParseArgs methods, similarly to the flag.Parse call.

The usage is automatically configured to show both sub commands and flags.

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.

  • Positional arguments are as any other flag: their number and type should be enforced and checked.

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

  • Usage format is standard, programs can't define their own format.

  • When flag configuration is wrong, the program will panic when starts. Most of them in flag definition stage, and not after flag parsing stage.

Examples

Definition and usage of sub commands and sub commands flags.

package main

import (
	"fmt"

	"github.com/posener/subcmd"
)

var (
	// Define a root command. Some options can be set using the `Opt*` functions. It returns a
	// `*Cmd` object.
	root = subcmd.Root()
	// The `*Cmd` object can be used as the standard library `flag.FlagSet`.
	flag0 = root.String("flag0", "", "root stringflag")
	// From each command object, a sub command can be created. This can be done recursively.
	sub1 = root.SubCommand("sub1", "first sub command")
	// Each sub command can have flags attached.
	flag1 = sub1.String("flag1", "", "sub1 string 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() {
	// In the example we use `Parse()` for a given list of command line arguments. This is useful
	// for testing, but should be replaced with `root.ParseArgs()` in `main()`
	root.Parse([]string{"cmd", "sub1", "-flag1", "value"})

	// Usually the program should switch over the sub commands. The chosen sub command will return
	// true for the `Parsed()` method.
	switch {
	case sub1.Parsed():
		fmt.Printf("Called sub1 with flag: %s", *flag1)
	case sub2.Parsed():
		fmt.Printf("Called sub2 with flag: %d", *flag2)
	}
}

Args

Usage of positional arguments. If a program accepts positional arguments it must declare it using the Args() or the ArgsVar() methods. Positional arguments can be also defined on sub commands.

package main

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

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

	// Should be in `main()`.
	root.Parse([]string{"cmd", "v1", "v2", "v3"})

	// Test:

	fmt.Println(*args)
}

Output:

[v1 v2 v3]

ArgsFn

Usage of positional arguments with a conversion function.

package main

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

func main() {
	// Should be defined in global `var`.
	var (
		root     = subcmd.Root()
		src, dst string
	)

	// A function that convert the positional arguments to the program variables.
	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()`.
	root.ArgsVar(subcmd.ArgsFn(argsFn), "[src] [dst]", "positional arguments for command line")

	// Should be in `main()`.
	root.Parse([]string{"cmd", "from.txt", "to.txt"})

	// Test:

	fmt.Println(src, dst)
}

Output:

from.txt to.txt

ArgsInt

Usage of positional arguments of a specific type.

package main

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

func main() {
	// Should be defined in global `var`.
	var (
		root = subcmd.Root()
		// Define positional arguments of type integer.
		args subcmd.ArgsInt
	)

	// Should be in `init()`.
	root.ArgsVar(&args, "[int...]", "numbers to sum")

	// Should be in `main()`.
	root.Parse([]string{"cmd", "10", "20", "30"})

	// Test:

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

Output:

60

ArgsN

Usage of positional arguments with exact number of arguments.

package main

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

func main() {
	// Should be defined in global `var`.
	var (
		root = subcmd.Root()
		// Define arguments with cap=2 will ensure that the number of arguments is always 2.
		args = make(subcmd.ArgsStr, 2)
	)

	// Should be in `init()`.
	root.ArgsVar(&args, "[src] [dst]", "positional arguments for command line")

	// Should be in `main()`.
	root.Parse([]string{"cmd", "from.txt", "to.txt"})

	// Test:

	fmt.Println(args)
}

Output:

[from.txt to.txt]


Created by goreadme

Documentation

Overview

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

Define a `root` command object using the `Root` 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` or `ParseArgs` methods, similarly to the `flag.Parse` call.

The usage is automatically configured to show both sub commands and flags.

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.

* Positional arguments are as any other flag: their number and type should be enforced and checked.

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

* Usage format is standard, programs can't define their own format.

* When flag configuration is wrong, the program will panic when starts. Most of them in flag definition stage, and not after flag parsing stage.

Example

Definition and usage of sub commands and sub commands flags.

package main

import (
	"fmt"

	"github.com/posener/subcmd"
)

var (
	// Define a root command. Some options can be set using the `Opt*` functions. It returns a
	// `*Cmd` object.
	root = subcmd.Root()
	// The `*Cmd` object can be used as the standard library `flag.FlagSet`.
	flag0 = root.String("flag0", "", "root stringflag")
	// From each command object, a sub command can be created. This can be done recursively.
	sub1 = root.SubCommand("sub1", "first sub command")
	// Each sub command can have flags attached.
	flag1 = sub1.String("flag1", "", "sub1 string 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() {
	// In the example we use `Parse()` for a given list of command line arguments. This is useful
	// for testing, but should be replaced with `root.ParseArgs()` in `main()`
	root.Parse([]string{"cmd", "sub1", "-flag1", "value"})

	// Usually the program should switch over the sub commands. The chosen sub command will return
	// true for the `Parsed()` method.
	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)

Usage of positional arguments. If a program accepts positional arguments it must declare it using the `Args()` or the `ArgsVar()` methods. Positional arguments can be also defined on sub commands.

package main

import (
	"fmt"

	"github.com/posener/subcmd"
)

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

	// Should be in `main()`.
	root.Parse([]string{"cmd", "v1", "v2", "v3"})

	// Test:

	fmt.Println(*args)
}
Output:
[v1 v2 v3]
Example (ArgsFn)

Usage of positional arguments with a conversion function.

package main

import (
	"fmt"

	"github.com/posener/subcmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root     = subcmd.Root()
		src, dst string
	)

	// A function that convert the positional arguments to the program variables.
	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()`.
	root.ArgsVar(subcmd.ArgsFn(argsFn), "[src] [dst]", "positional arguments for command line")

	// Should be in `main()`.
	root.Parse([]string{"cmd", "from.txt", "to.txt"})

	// Test:

	fmt.Println(src, dst)
}
Output:
from.txt to.txt
Example (ArgsInt)

Usage of positional arguments of a specific type.

package main

import (
	"fmt"

	"github.com/posener/subcmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = subcmd.Root()
		// Define positional arguments of type integer.
		args subcmd.ArgsInt
	)

	// Should be in `init()`.
	root.ArgsVar(&args, "[int...]", "numbers to sum")

	// Should be in `main()`.
	root.Parse([]string{"cmd", "10", "20", "30"})

	// Test:

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

Usage of positional arguments with exact number of arguments.

package main

import (
	"fmt"

	"github.com/posener/subcmd"
)

func main() {
	// Should be defined in global `var`.
	var (
		root = subcmd.Root()
		// Define arguments with cap=2 will ensure that the number of arguments is always 2.
		args = make(subcmd.ArgsStr, 2)
	)

	// Should be in `init()`.
	root.ArgsVar(&args, "[src] [dst]", "positional arguments for command line")

	// Should be in `main()`.
	root.Parse([]string{"cmd", "from.txt", "to.txt"})

	// Test:

	fmt.Println(args)
}
Output:
[from.txt to.txt]

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func OptDetails added in v1.1.0

func OptDetails(details string) optionFn

OptSynopsis sets a 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 added in v1.1.0

func OptSynopsis(synopsis string) optionRootFn

OptSynopsis sets a description to the root command.

Types

type ArgsFn added in v1.1.0

type ArgsFn func([]string) error

ArgsFn is a function that implements Args. Usage example:

var (
	cmd      = subcmd.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() {
	cmd.ArgsVar(subcmd.ArgsFn(setArgs), "[src] [dst]", "define source and destination")
}

func (ArgsFn) Set added in v1.1.0

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

type ArgsInt added in v1.1.0

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 := subcmd.Root()

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

To get a list of specific number of integers:

root := subcmd.Root()

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

func (*ArgsInt) Set added in v1.1.0

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

type ArgsStr added in v1.1.0

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 := subcmd.Root()

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

To get a list of specific number of arguments:

root := subcmd.Root()

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

func (*ArgsStr) Set added in v1.1.0

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

type ArgsValue added in v1.1.0

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 Root

func Root(options ...optionRoot) *Cmd

Root creates a new root command.

func (*Cmd) Parse

func (c *Cmd) Parse(args []string) error

Parse a set of arguments.

func (*Cmd) ParseArgs

func (c *Cmd) ParseArgs() error

Parse command line arguments.

type SubCmd added in v1.2.0

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

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

func (*SubCmd) Args added in v1.2.0

func (c *SubCmd) Args(usage, details string) *[]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 added in v1.2.0

func (c *SubCmd) ArgsVar(value ArgsValue, usage, details string)

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

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

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

func (*SubCmd) Bool added in v1.2.0

func (c *SubCmd) Bool(name string, value bool, usage string) *bool

func (*SubCmd) BoolVar added in v1.2.0

func (c *SubCmd) BoolVar(p *bool, name string, value bool, usage string)

func (*SubCmd) Duration added in v1.2.0

func (c *SubCmd) Duration(name string, value time.Duration, usage string) *time.Duration

func (*SubCmd) DurationVar added in v1.2.0

func (c *SubCmd) DurationVar(p *time.Duration, name string, value time.Duration, usage string)

func (*SubCmd) Float64 added in v1.2.0

func (c *SubCmd) Float64(name string, value float64, usage string) *float64

func (*SubCmd) Float64Var added in v1.2.0

func (c *SubCmd) Float64Var(p *float64, name string, value float64, usage string)

func (*SubCmd) Int added in v1.2.0

func (c *SubCmd) Int(name string, value int, usage string) *int

func (*SubCmd) Int64 added in v1.2.0

func (c *SubCmd) Int64(name string, value int64, usage string) *int64

func (*SubCmd) Int64Var added in v1.2.0

func (c *SubCmd) Int64Var(p *int64, name string, value int64, usage string)

func (*SubCmd) IntVar added in v1.2.0

func (c *SubCmd) IntVar(p *int, name string, value int, usage string)

func (*SubCmd) Parsed added in v1.2.0

func (c *SubCmd) Parsed() bool

func (*SubCmd) Set added in v1.2.0

func (c *SubCmd) Set(name, value string) error

func (*SubCmd) String added in v1.2.0

func (c *SubCmd) String(name string, value string, usage string) *string

func (*SubCmd) StringVar added in v1.2.0

func (c *SubCmd) StringVar(p *string, name string, value string, usage string)

func (*SubCmd) SubCommand added in v1.2.0

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

SubCommand creates a new sub command to the given command.

func (*SubCmd) Uint added in v1.2.0

func (c *SubCmd) Uint(name string, value uint, usage string) *uint

func (*SubCmd) Uint64 added in v1.2.0

func (c *SubCmd) Uint64(name string, value uint64, usage string) *uint64

func (*SubCmd) UintVar added in v1.2.0

func (c *SubCmd) UintVar(p *uint, name string, value uint, usage string)

func (*SubCmd) UintVar64 added in v1.2.0

func (c *SubCmd) UintVar64(p *uint64, name string, value uint64, usage string)

func (*SubCmd) Usage added in v1.2.0

func (c *SubCmd) Usage()

func (*SubCmd) Var added in v1.2.0

func (c *SubCmd) Var(value flag.Value, name string, usage string)

func (*SubCmd) Visit added in v1.2.0

func (c *SubCmd) Visit(fn func(*flag.Flag))

func (*SubCmd) VisitAll added in v1.2.0

func (c *SubCmd) VisitAll(fn func(*flag.Flag))

Jump to

Keyboard shortcuts

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