cli

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Dec 16, 2025 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package cli provides utilities for building command-line interfaces from wrapped functions.

Overview

The cli package enables you to create command dispatchers that parse command-line arguments and execute wrapped functions. It supports both single-level and multi-level command hierarchies, automatic help text generation, and shell completion.

Basic Usage

Create a simple CLI with commands:

func Deploy(env, service string, version int) error {
    fmt.Printf("Deploying %s v%d to %s\n", service, version, env)
    return nil
}

func main() {
    dispatcher := cli.NewStringArgsDispatcher("myapp")
    dispatcher.MustAddCommand("deploy", "Deploy a service",
        function.MustReflectWrapper("deploy", Deploy))

    err := dispatcher.DispatchCombinedCommandAndArgs(context.Background(), os.Args[1:])
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v\n", err)
        os.Exit(1)
    }
}

Usage:

$ myapp deploy production api-server 42
Deploying api-server v42 to production

Multi-level Commands

Create nested command hierarchies:

dispatcher := cli.NewSuperStringArgsDispatcher("myapp")

// Add user commands
userCmd := dispatcher.MustAddSuperCommand("user")
userCmd.MustAddCommand("create", "Create a new user",
    function.MustReflectWrapper("CreateUser", CreateUser))
userCmd.MustAddCommand("delete", "Delete a user",
    function.MustReflectWrapper("DeleteUser", DeleteUser))

// Add db commands
dbCmd := dispatcher.MustAddSuperCommand("db")
dbCmd.MustAddCommand("migrate", "Run migrations",
    function.MustReflectWrapper("Migrate", Migrate))

dispatcher.DispatchCombinedCommandAndArgs(context.Background(), os.Args[1:])

Usage:

$ myapp user create alice alice@example.com
$ myapp user delete alice
$ myapp db migrate

Default Commands

You can register a default command that runs when no command is specified:

dispatcher.MustAddDefaultCommand("Run the server",
    function.MustReflectWrapper("RunServer", RunServer))

Now running the program without arguments will execute the default command:

$ myapp
Server started on :8080

Help and Usage

Automatically print available commands:

if len(os.Args) == 1 || os.Args[1] == "help" {
    dispatcher.PrintCommandsUsageIntro()
    os.Exit(0)
}

This prints formatted help text with command names, argument types, and descriptions.

Shell Completion

Enable shell completion for your CLI:

func main() {
    dispatcher := cli.NewStringArgsDispatcher("myapp")
    // ... add commands ...

    // Enable completion
    cli.CompleteStringArgsDispatcher(dispatcher)

    // Normal dispatch logic
    dispatcher.DispatchCombinedCommandAndArgs(context.Background(), os.Args[1:])
}

Users can then install completion:

$ myapp --install-completion
$ myapp de<TAB>  # completes to "deploy"

Logging

Track command execution with loggers:

logger := cli.StringArgsCommandLoggerFunc(func(command string, args []string) {
    log.Printf("Executing: %s with args %v", command, args)
})

dispatcher := cli.NewStringArgsDispatcher("myapp", logger)

Customization

Customize output colors:

import "github.com/fatih/color"

cli.UsageColor = color.New(color.FgGreen)
cli.DescriptionColor = color.New(color.FgYellow)

Error Handling

The package provides specific error types for better error handling:

err := dispatcher.Dispatch(ctx, "unknown-command")
if cli.IsErrCommandNotFound(err) {
    fmt.Println("Command not found. Available commands:")
    dispatcher.PrintCommands()
}

Result Handlers

Process function results before they're displayed:

resultHandler := function.ResultsHandlerFunc(func(results []any) ([]any, error) {
    // Transform or validate results
    return results, nil
})

dispatcher.MustAddCommand("query", "Query the database",
    wrapper, resultHandler)

Best Practices

  • Use descriptive command names (verbs like "create", "delete", "list")
  • Provide clear descriptions for commands and arguments
  • Handle errors gracefully with appropriate exit codes
  • Use default commands sparingly (only for single-purpose CLIs)
  • Enable shell completion for better user experience
  • Log command execution in production applications

Index

Constants

View Source
const (
	DefaultCommand = ""
)

Variables

View Source
var (
	// UsageColor is the color in which the
	// command usage will be printed on the screen.
	UsageColor = color.New(color.FgHiCyan)

	// DescriptionColor is the color in which the
	// command usage description will be printed on the screen.
	DescriptionColor = color.New(color.FgCyan)
)

Functions

func CompleteStringArgsDispatcher

func CompleteStringArgsDispatcher(disp *StringArgsDispatcher)

func CompleteSuperStringArgsDispatcher

func CompleteSuperStringArgsDispatcher(disp *SuperStringArgsDispatcher)

func IsErrCommandNotFound

func IsErrCommandNotFound(err error) bool

IsErrCommandNotFound returns true if the passed error can be unwrapped to either ErrCommandNotFound or ErrSuperCommandNotFound.

Types

type ErrCommandNotFound

type ErrCommandNotFound string

func (ErrCommandNotFound) Error

func (e ErrCommandNotFound) Error() string

type ErrSuperCommandNotFound

type ErrSuperCommandNotFound string

func (ErrSuperCommandNotFound) Error

func (e ErrSuperCommandNotFound) Error() string

type StringArgsCommandLogger

type StringArgsCommandLogger interface {
	LogStringArgsCommand(command string, args []string)
}

StringArgsCommandLogger provides a way to log or track command executions. Implementations can write to log files, send metrics, or perform auditing.

type StringArgsCommandLoggerFunc

type StringArgsCommandLoggerFunc func(command string, args []string)

StringArgsCommandLoggerFunc is a function type that implements StringArgsCommandLogger. It allows standalone functions to be used as loggers.

func (StringArgsCommandLoggerFunc) LogStringArgsCommand

func (f StringArgsCommandLoggerFunc) LogStringArgsCommand(command string, args []string)

type StringArgsDispatcher

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

StringArgsDispatcher dispatches CLI commands to wrapped functions. It maintains a registry of commands and their associated functions, handles command-line argument parsing, and executes the appropriate function.

Example:

dispatcher := cli.NewStringArgsDispatcher("myapp")
dispatcher.MustAddCommand("deploy", "Deploy a service", deployWrapper)
dispatcher.DispatchCombinedCommandAndArgs(ctx, os.Args[1:])

func NewStringArgsDispatcher

func NewStringArgsDispatcher(baseCommand string, loggers ...StringArgsCommandLogger) *StringArgsDispatcher

NewStringArgsDispatcher creates a new command dispatcher. The baseCommand is used for help text and completion (typically the program name). Optional loggers are called whenever a command is executed.

Example:

logger := cli.StringArgsCommandLoggerFunc(func(cmd string, args []string) {
    log.Printf("Executing: %s %v", cmd, args)
})
dispatcher := cli.NewStringArgsDispatcher("myapp", logger)

func (*StringArgsDispatcher) AddCommand

func (disp *StringArgsDispatcher) AddCommand(command, description string, commandFunc function.Wrapper, resultsHandlers ...function.ResultsHandler) error

AddCommand registers a new command with the dispatcher. Returns an error if the command name is invalid or already registered.

Parameters:

  • command: The command name (e.g., "deploy", "create", "delete")
  • description: Human-readable description for help text
  • commandFunc: The wrapped function to execute
  • resultsHandlers: Optional handlers to process function results

Example:

err := dispatcher.AddCommand("deploy", "Deploy a service",
    function.MustReflectWrapper("deploy", Deploy))

func (*StringArgsDispatcher) AddDefaultCommand

func (disp *StringArgsDispatcher) AddDefaultCommand(description string, commandFunc function.Wrapper, resultsHandlers ...function.ResultsHandler) error

AddDefaultCommand registers a command that runs when no command is specified. This is useful for single-purpose CLIs or when you want a default action.

Example:

// When user runs just "myapp" with no arguments
dispatcher.AddDefaultCommand("Run the server",
    function.MustReflectWrapper("serve", Serve))

func (*StringArgsDispatcher) Commands

func (disp *StringArgsDispatcher) Commands() []string

Commands returns a sorted list of all registered command names.

func (*StringArgsDispatcher) Dispatch

func (disp *StringArgsDispatcher) Dispatch(ctx context.Context, command string, args ...string) error

Dispatch executes the specified command with the given arguments. The context is passed to the wrapped function if it accepts one. Loggers are notified before execution.

Returns ErrCommandNotFound if the command doesn't exist, or any error returned by the wrapped function.

Example:

err := dispatcher.Dispatch(ctx, "deploy", "production", "api-server", "42")

func (*StringArgsDispatcher) DispatchCombinedCommandAndArgs

func (disp *StringArgsDispatcher) DispatchCombinedCommandAndArgs(ctx context.Context, commandAndArgs []string) (command string, err error)

DispatchCombinedCommandAndArgs parses and dispatches from os.Args style input. The first element is treated as the command name, and the rest as arguments. If commandAndArgs is empty, the default command is executed.

This is the typical entry point for CLI applications:

command, err := dispatcher.DispatchCombinedCommandAndArgs(ctx, os.Args[1:])
if err != nil {
    fmt.Fprintf(os.Stderr, "Error in %s: %v\n", command, err)
    os.Exit(1)
}

func (*StringArgsDispatcher) DispatchDefaultCommand

func (disp *StringArgsDispatcher) DispatchDefaultCommand() error

DispatchDefaultCommand executes the default command with a background context.

func (*StringArgsDispatcher) HasCommand added in v0.5.0

func (disp *StringArgsDispatcher) HasCommand(command string) bool

HasCommand returns true if a command with the given name is registered.

func (*StringArgsDispatcher) HasDefaultCommand added in v0.5.0

func (disp *StringArgsDispatcher) HasDefaultCommand() bool

HasDefaultCommand returns true if a default command is registered.

func (*StringArgsDispatcher) MustAddCommand

func (disp *StringArgsDispatcher) MustAddCommand(command, description string, commandFunc function.Wrapper, resultsHandlers ...function.ResultsHandler)

MustAddCommand is like AddCommand but panics on error. Use this in initialization code where command registration failures should be fatal.

func (*StringArgsDispatcher) MustAddDefaultCommand

func (disp *StringArgsDispatcher) MustAddDefaultCommand(description string, commandFunc function.Wrapper, resultsHandlers ...function.ResultsHandler)

MustAddDefaultCommand is like AddDefaultCommand but panics on error.

func (*StringArgsDispatcher) MustDispatch

func (disp *StringArgsDispatcher) MustDispatch(ctx context.Context, command string, args ...string)

MustDispatch is like Dispatch but panics on error.

func (*StringArgsDispatcher) MustDispatchCombinedCommandAndArgs

func (disp *StringArgsDispatcher) MustDispatchCombinedCommandAndArgs(ctx context.Context, commandAndArgs []string) (command string)

MustDispatchCombinedCommandAndArgs is like DispatchCombinedCommandAndArgs but panics on error.

func (*StringArgsDispatcher) MustDispatchDefaultCommand

func (disp *StringArgsDispatcher) MustDispatchDefaultCommand()

MustDispatchDefaultCommand is like DispatchDefaultCommand but panics on error.

func (*StringArgsDispatcher) PrintCommands

func (disp *StringArgsDispatcher) PrintCommands()

PrintCommands prints all registered commands with their descriptions and argument types. Output is colorized using UsageColor and DescriptionColor. This is useful for generating help text.

func (*StringArgsDispatcher) PrintCommandsUsageIntro

func (disp *StringArgsDispatcher) PrintCommandsUsageIntro()

PrintCommandsUsageIntro prints a "Commands:" header followed by all commands. Does nothing if no commands are registered.

func (*StringArgsDispatcher) PrintCompletion added in v0.5.0

func (disp *StringArgsDispatcher) PrintCompletion(args []string)

PrintCompletion prints commands matching the given prefix for shell completion. This is used internally by the completion system.

type SuperStringArgsDispatcher

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

func NewSuperStringArgsDispatcher

func NewSuperStringArgsDispatcher(baseCommand string, loggers ...StringArgsCommandLogger) *SuperStringArgsDispatcher

func (*SuperStringArgsDispatcher) AddCommand

func (disp *SuperStringArgsDispatcher) AddCommand(command, description string, commandFunc function.Wrapper, resultsHandlers ...function.ResultsHandler) error

func (*SuperStringArgsDispatcher) AddDefaultCommand

func (disp *SuperStringArgsDispatcher) AddDefaultCommand(description string, commandFunc function.Wrapper, resultsHandlers ...function.ResultsHandler) error

func (*SuperStringArgsDispatcher) AddSuperCommand

func (disp *SuperStringArgsDispatcher) AddSuperCommand(superCommand string) (subDisp *StringArgsDispatcher, err error)

func (*SuperStringArgsDispatcher) Commands

func (disp *SuperStringArgsDispatcher) Commands() []string

func (*SuperStringArgsDispatcher) Dispatch

func (disp *SuperStringArgsDispatcher) Dispatch(ctx context.Context, superCommand, command string, args ...string) error

func (*SuperStringArgsDispatcher) DispatchCombinedCommandAndArgs

func (disp *SuperStringArgsDispatcher) DispatchCombinedCommandAndArgs(ctx context.Context, commandAndArgs []string) (superCommand, command string, err error)

func (*SuperStringArgsDispatcher) DispatchDefaultCommand

func (disp *SuperStringArgsDispatcher) DispatchDefaultCommand() error

func (*SuperStringArgsDispatcher) HasCommand added in v0.5.0

func (disp *SuperStringArgsDispatcher) HasCommand(superCommand string) bool

func (*SuperStringArgsDispatcher) HasSubCommand

func (disp *SuperStringArgsDispatcher) HasSubCommand(superCommand, command string) bool

func (*SuperStringArgsDispatcher) MustAddCommand

func (disp *SuperStringArgsDispatcher) MustAddCommand(command, description string, commandFunc function.Wrapper, resultsHandlers ...function.ResultsHandler)

func (*SuperStringArgsDispatcher) MustAddDefaultCommand

func (disp *SuperStringArgsDispatcher) MustAddDefaultCommand(description string, commandFunc function.Wrapper, resultsHandlers ...function.ResultsHandler)

func (*SuperStringArgsDispatcher) MustAddSuperCommand

func (disp *SuperStringArgsDispatcher) MustAddSuperCommand(superCommand string) (subDisp *StringArgsDispatcher)

func (*SuperStringArgsDispatcher) MustDispatch

func (disp *SuperStringArgsDispatcher) MustDispatch(ctx context.Context, superCommand, command string, args ...string)

func (*SuperStringArgsDispatcher) MustDispatchCombinedCommandAndArgs

func (disp *SuperStringArgsDispatcher) MustDispatchCombinedCommandAndArgs(ctx context.Context, commandAndArgs []string) (superCommand, command string)

func (*SuperStringArgsDispatcher) MustDispatchDefaultCommand

func (disp *SuperStringArgsDispatcher) MustDispatchDefaultCommand()

func (*SuperStringArgsDispatcher) PrintCommands

func (disp *SuperStringArgsDispatcher) PrintCommands()

func (*SuperStringArgsDispatcher) PrintCommandsUsageIntro

func (disp *SuperStringArgsDispatcher) PrintCommandsUsageIntro()

func (*SuperStringArgsDispatcher) PrintCompletion added in v0.5.0

func (disp *SuperStringArgsDispatcher) PrintCompletion(args []string)

func (*SuperStringArgsDispatcher) SubCommands

func (disp *SuperStringArgsDispatcher) SubCommands(superCommand string) []string

Jump to

Keyboard shortcuts

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