gommand

package module
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Mar 6, 2025 License: Apache-2.0 Imports: 9 Imported by: 9

README

Gommand

A simple library for making CLI tools.

Acknowledging my elders: this tool is heavily inspired by Cobra/Viper, with just a touch of urfave/cli.


YAGCLL

Maybe a more fitting name would be: Yet Another Go Command Line Library. With libraries like Cobra/Viper and urfave/cli, why do we need Yet Another One? The decision to write my own library was made for 2 reasons:

  • First, and most importantly: I just wanted to.
  • Second: All the clis I have made using Cobra/Viper require the same boiler plate to configure flags from env vars. Rather than continuing to require the extra steps, I just made these behaviors the default.
  • Third (because who doesn't love an off-by-one error): Viper has a large dependency graph because it does a lot of things I never need, namely: flag values from config files.

To summarize: I wanted a smaller dependency footprint that required less boiler plate for my base use case.

Usage

For more usage examples, check the examples directory.

package main

import (
	"fmt"
	"os"
	"strconv"

	"github.com/jimmykodes/gommand"
)

var rootCmd = &gommand.Command{
	Name:         "sum  [...n]",
	Usage:        "sum all provided numbers",
	Version:      "1.0.0",
	ArgValidator: gommand.ArgsEvery(gommand.ArgsMin(1), ints),
	Run: func(ctx *gommand.Context) error {
		var total int
		for _, s := range ctx.Args() {
			i, err := strconv.Atoi(s)
			if err != nil {
				return err
			}
			total += i
		}
		fmt.Println(total)
		return nil
	},
}

func ints(s []string) error {
	for _, arg := range s {
		if _, err := strconv.Atoi(arg); err != nil {
			return fmt.Errorf("%s is not an integer", arg)
		}
	}
	return nil
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNoRunner     = errors.New("gommand: command has no run function")
	ErrNoSubcommand = errors.New("gommand: must specify a subcommand")
)

Functions

This section is empty.

Types

type ArgValidator

type ArgValidator func(s []string) error

func ArgsAny

func ArgsAny() ArgValidator

func ArgsBetween

func ArgsBetween(lower, upper int) ArgValidator

func ArgsEvery

func ArgsEvery(validators ...ArgValidator) ArgValidator

func ArgsExact

func ArgsExact(n int) ArgValidator

func ArgsMax

func ArgsMax(bound int) ArgValidator

func ArgsMin

func ArgsMin(bound int) ArgValidator

func ArgsNone

func ArgsNone() ArgValidator

func ArgsSome

func ArgsSome(validators ...ArgValidator) ArgValidator

type Command

type Command struct {
	// Name is the name of the command.
	// This is ignored if the command is invoked using Execute or ExecuteContext, but if registered as a subcommand
	// of a function, the name defines how the subcommand is called.
	// ex:
	// c1 := &Command{Name: "entrypoint"}
	// c2 := &Command{Name: "my-sub-command", Run: func(*Context) error { fmt.Println("sub"); return nil }}
	//
	// c1.SubCommand(c2)
	//
	// func main() {
	// 		_ = c1.Execute()
	// }
	//
	// c2 would be called by running
	// entrypoint my-sub-command
	//
	// Anything included after a space is expected to be usage descriptions
	// General syntax guidance
	//   ... indicates multiple of the preceding argument can be provided
	//   [ ] indicates optional arguments
	//   { } indicates a set of mutually exclusive required arguments
	//   |   indicates mutually exclusive arguments, where only one value in the set
	//       should be provided at a time. As described above, if the set of arguments
	//       are optional, the set should be enclosed in [ ] otherwise they should be
	//       enclosed in { }
	//
	// Example: create {--from-file file | --from-gcs bucket} [-d destination] file_name...
	Name string

	// Usage is the short explanation of the command
	Usage string

	// Description is the longer description of the command printed out by the help text
	Description string

	// Aliases are aliases for the current command.
	//
	// Ex:
	// c1 := &Command{Name: "items"}
	// c2 := &Command{Name: "list", Aliases: []string{"ls", "l"}}
	// c1.SubCommand(c2)
	//
	// items list
	// items ls
	// items l
	//
	// All are valid ways of executing the `list` command
	Aliases []string

	// Version is the value that will be printed when `--version` is passed to the command.
	// When retrieving the command version, the call tree is traversed backwards until a Command
	// is reached that has a non-zero value for the version. This means that it is possible
	// to version individual branches of the call tree, though this is not recommended. It is
	// intended to be set at the root of the tree, ideally through a package level var that can
	// be set using ldflags at build time
	// ie: go build -ldflags="cmd.Version=1.1.0"
	Version string

	// ArgValidator is an ArgValidator to be called on the args of the function being executed. This is called before any of
	// the functions for this command are called.
	// If this is not defined ArgsNone is used.
	ArgValidator ArgValidator

	// Flags are a slice of flags.Flag that will be used to initialize the command's FlagSet
	FlagSet *flags.FlagSet

	// PersistentFlags are a slice of flags.Flag that will be used to initialize the command's PersistentFlagSet
	PersistentFlagSet *flags.FlagSet

	// Run is the core function the command should execute
	Run func(*Context) error

	// PreRun will run immediately before Run, if defined
	PreRun func(*Context) error
	// PostRun will run immediately after Run if defined and either Run exits with no error or DeferPost is true
	PostRun func(*Context) error

	// PersistentPreRun is a function that will run before PreRun and will be run for any subcommands of this command.
	//
	// PersistentPreRun commands are executed in FIFO order
	//
	// ex:
	// c1 := &Command{Name: "c1", PersistentPreRun: func(*Context) error { fmt.Println("c1"); return nil }}
	// c2 := &Command{Name: "c2", PersistentPreRun: func(*Context) error { fmt.Println("c2"); return nil }}
	// c3 := &Command{Name: "c3", Run: func(*Context) error { fmt.Println("c3"); return nil }}
	//
	// c1.SubCommand(c2)
	// c2.SubCommand(c3)
	//
	// When c3 is run, stdout will see
	// c1
	// c2
	// c3
	//
	// If any of the nested commands return an error, all execution is stopped and that error is returned.
	PersistentPreRun func(*Context) error

	// PersistentPostRun is a function that will run after PostRun and will be run for any subcommands of this command.
	//
	// PersistentPostRun commands are executed in LIFO order
	//
	// ex:
	// c1 := &Command{Name: "c1", PersistentPostRun: func(*Context) error { fmt.Println("c1"); return nil }}
	// c2 := &Command{Name: "c2", PersistentPostRun: func(*Context) error { fmt.Println("c2"); return nil }}
	// c3 := &Command{Name: "c3", Run: func(*Context) error { fmt.Println("c3"); return nil }}
	//
	// c1.SubCommand(c2)
	// c2.SubCommand(c3)
	//
	// when c3 is run, stdout will see
	// c3
	// c2
	// c1
	//
	// If any of the nested commands return an error, execution will stop and the error will be returned, unless DeferPost
	// is true, in which case the error will be recorded and returned at the end, but the remaining functions will still
	// execute
	PersistentPostRun func(*Context) error

	// DeferPost will defer PersistentPostRun and PostRun functions.
	// This will cause them to run even if the Run function exits with an error.
	// Setting this value is persistent, meaning any subcommands from where this is set will
	// also defer their post run functions
	DeferPost bool

	// SilenceHelp will not print the help message if the command exits with an error.
	// This field will propagate to subcommands and cannot be overwritten by the child, so if any
	// point of a command's upstream lineage has the value set, the help message will be silenced
	SilenceHelp bool

	// SilenceError is like SilenceHelp but does not print the "Error: xxx" message when the command
	// exits with an error
	SilenceError bool
	// contains filtered or unexported fields
}

Command represents a command line command

The order of functions is:

  • PersistentPreRun -- see note on field
  • PreRun
  • Run
  • PostRun
  • PersistentPostRun -- see note of field

if PersistentPreRun or PreRun return an error, execution is stopped and the error is returned if Run returns an error and DeferPost is false, execution is stopped and the error is returned if DeferPost is true, PostRun and PersistentPostRun will be executed even if Run returns an error

func (*Command) Execute

func (c *Command) Execute() error

func (*Command) ExecuteContext

func (c *Command) ExecuteContext(ctx context.Context) error

func (*Command) SubCommand

func (c *Command) SubCommand(cmds ...*Command)

type Context

type Context struct {
	context.Context
	// contains filtered or unexported fields
}

func (*Context) Arg added in v0.16.0

func (c *Context) Arg(idx int) string

Arg will return the command line argument at the given index Returns an empty string if idx is out of range

func (*Context) Args

func (c *Context) Args() []string

func (*Context) Flags

func (c *Context) Flags() *flags.FlagGetter

Jump to

Keyboard shortcuts

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