cmder

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Feb 14, 2023 License: Apache-2.0, MIT, Apache-2.0, + 1 more Imports: 4 Imported by: 4

README

cmder - lightweight Go pattern for writing CLIs Hexops logo

Go Reference

Go CI Go Report Card

Cmder is a ~100 LOC pattern that has been used as the foundation of all the Go CLIs I've written (including the Sourcegraph CLI.)

I've often just suggested others simply copy the pattern as it is so lightweight. Now you can import it as a Go package, which helps to document the pattern you're using, and avoids temptation to make it more complex than needed.

  • Mimics what the official go tool does internally.
  • Merely builds upon the flag package to support subcommands.
  • Supports subcommand flags, subcommands of subcommands, etc.

Example

An example/fictional HTTP tool called kurl is provided in the example/kurl directory.

Usage

The idea is simple, import the package:

import "github.com/hexops/cmder"

Declare a list of your subcommands:

// commands contains all registered subcommands.
var commands cmder.Commander

Append a few subcommands like so (usually one init function per subcommand):

flagSet := flag.NewFlagSet("foo", flag.ExitOnError)
commands = append(commands, &cmder.Command{
    FlagSet: flagSet,
    Handler: func(args []string) error {
        _ = flagSet.Parse(args)
        return nil
    },
})

In your main function, call:

commands.Run(flag.CommandLine, commandName, usageText, os.Args[1:])
  • Consult go help for inspiration on how to write your usageText.
  • Register subcommand flags by using e.g. flagSet.Bool (as you would've if using the Go flag package otherwise.)
  • Need subcommands in your subcommands? Declare another set of commands and simply call the Run method inside your subcommand Handler.

Consult the API documentation for more information.

Project status

We're open to considering improvements, but since this pattern has been in use in various CLIs over the past 3-4 years, we likely won't make any major changes to the API or introduce new features. The aim is to keep it minimal and simple.

Some popular alternatives which aim to be simple:

Some popular alternatives which provide as many features and knobs as you could want include:

Version history

v1.0.2

Fixed an issue where subcommands incorrectly had their flags parsed twice. Handlers should always call flagSet.Parse(args) on the arguments passed to them, as demonstrated in example/kurl/get.go.

v1.0.1

Initial release.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Command

type Command struct {
	// FlagSet is the flag set for the command.
	FlagSet *flag.FlagSet

	// Aliases for the command name.
	Aliases []string

	// Handler is the function that is invoked to handle this command.
	//
	// The error types *UsageError and *ExitCodeError have special meaning, consult their docs for
	// details.
	Handler func(args []string) error

	// A flagSet.Usage function to invoke when e.g. the -h flag is specified. If nil, a default one
	// is used.
	UsageFunc func()
}

Command is a subcommand handler and its associated flag set.

type Commander

type Commander []*Command

Commander represents a command with a list of subcommands.

func (Commander) Run

func (c Commander) Run(flagSet *flag.FlagSet, cmdName, usageText string, args []string)

Run runs a subcommand of the command described by the input flagSet (e.g. flag.CommandLine).

cmdName and usageText should describe your command, not the subcommand. Consult "go help" for inspiration when writing your own usageText.

A special "help" command is registered automatically, which acts the same as the `-h` flag.

type ExitCodeError

type ExitCodeError struct {
	// Err is the error to log when exiting.
	Err error

	// ExitCode is the exit status code to use in the call to os.Exit.
	ExitCode int
}

ExitCodeError is an error type that subcommands can return in order to specify the exact exit code.

func (*ExitCodeError) Error

func (e *ExitCodeError) Error() string

Error implements the error interface.

type UsageError

type UsageError struct {
	// Err is the error to log when exiting.
	Err error
}

UsageError is an error type that subcommands can return in order to signal that a usage error has occurred.

func (*UsageError) Error

func (e *UsageError) Error() string

Error implements the error interface.

Directories

Path Synopsis
example
kurl command

Jump to

Keyboard shortcuts

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