Help us shape the future of Go! Take the 2026 Go Developer Survey

getoptx

package module
v0.0.0-...-d70c8f9 Latest Latest
Warning

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

Go to latest
Published: Mar 29, 2022 License: ISC Imports: 14 Imported by: 8

README

github.com/bassosimone/getoptx

Yet another command line options parser.

Documentation

Overview

Package getoptx implements a command line parser. Under the hood, we use the excellent github.com/pborman/getopt/v2 library.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrTooManyPositionalArguments indicates that you passed too many
	// positional arguments to the current parser.
	ErrTooManyPositionalArguments = errors.New("too many positional arguments")

	// ErrTooFewPositionalArguments indicates that you passed too few
	// positional arguments to the current parser.
	ErrTooFewPositionalArguments = errors.New("too few positional arguments")
)
View Source
var ErrNoSuchSubcommand = errors.New("no such subcommand")

ErrNoSuchSubcommand indicates that we don't know a subcommand with that name.

Functions

This section is empty.

Types

type CommandParser

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

CommandParser is a parser for a command or a subcommand. You construct this type using Command (for a top-level command) or Subcommand.

Once you have constructed a CommandParser you call Getopt or MustGetopt to parse the command line arguments and get the SelectedSubcommand.

See Subcommand for more details on the typical usage.

func Command

func Command(
	description string, options interface{}, subcommands ...*CommandParser) *CommandParser

Command creates the toplevel command for the whole program with the given description, the given options, and zero or more subcommands. This function also register an internal subcommand implementing the `help` subcommand unless you have already included one such command in subcommands. Apart from that, this function is equivalent to calling Subcommand with os.Args[0] as the first argument followed by the arguments passed to Command. For this reason, please see Subcommand for more information about the usage.

func LeafSubcommand

func LeafSubcommand(
	name, description string, options interface{}, config ...Config) *CommandParser

LeafSubcommand creates a subcommand that does not take any further subcommand (i.e., a "leaf" subcommand in the commands tree).

You can use Configs such as NoPositionalArguments() to control the leaf subcommand behavior in terms of positional arguments. This function will emit a warning and otherwise ignore any piece of config that does not specifically deal with controlling positional arguments.

See Subcommand's docs for further information.

func Subcommand

func Subcommand(name, description string, options interface{},
	subcommands ...*CommandParser) *CommandParser

Subcommand creates a new subcommand with the given name, the given description, the given options, and zero or more subcommands.

The typical usage for Command, Subcommand, and LeafSubcommand calls for creating data structures containing options you'd like to fill and for instantiating a complex command line parser by chaining calls to Command, Subcommand, or LeafSubcommand depending on your needs.

Here's an example:

type GlobalOptions struct {
  Batch     bool     `doc:"emit JSON messages" short:"b"`
  Logfile   string   `doc:"file where to write logs" short:"L"`
  Verbose   bool     `doc:"run in verbose mode" short:"v"`
}

type WebsitesOptions struct {
  ForceHTTP3 bool `doc:"forces using HTTP3" short:"3"`
}

type URLGetterOptions struct {
  SNI  string `doc:"forces using a specific SNI"`
  Host string `doc:"forces using a specific host header"`
}

type RunOptions struct {
  Input     string           `doc:"add URL to measure"`
  InputFile string           `doc:"add file from which to read URLs"`
  Websites  WebsitesOptions  `doc:"-"`  // <- (3)
  URLGetter URLGetterOptions `doc:"-"`
}

type ListOptions struct {
  ID int `doc:"ID of the result to show" short:"I"`
}

type RmOptions struct {
  ID int `doc:"ID of the result to remove" short:"I"`
}

type Options struct {
  Global GlobalOptions
  Run    RunOptions
  Rm     RmOptions
  List   ListOptions
}

options := &Options{}
cli := getoptx.Command(
  "Network measurement tool",
  &options.Global,
  getoptx.Subcommand(
    "run", "Runs network measurements", &options.RunOptions,
    getoptx.LeafCommand(
      "websites", "Tests websites for censorship", &options.Websites,
      getoptx.NoPositionalArguments(),
    ),
    getoptx.LeafCommand(
      "urlgetter", "Swiss-knife measurements tool", &options.URLGetter,
      getoptx.NoPositionalArguments(),
    ),
  ),
  getoptx.LeafCommand(
    "list", "Lists network measurements", &options.ListOptions,
  ),
  getoptx.LeafCommand(
    "rm", "Remove network measurements", &options.RmOptions,
  ),
)

selected := cli.MustGetopt(os.Args)
switch selected.Options().(type) { // <- (2)
case *WebsitesOptions:
  Websites(options)
case *URLGetterOptions:
  URLGetter(options)
case *ListOptions:
  List(options)
case *RmOptions:
  Rm(options)
case *getoptx.HasPrintedHelp:  // <- (1)
  os.Exit(0)
default:
  log.Fatalf("unhandled selected option: %T %+v", selected, selected)
}

This example shows the most complex usage possible. In particular, here you see the following functionality in action:

1. MustGetopt automatically prints contextual help for -h/--help as well as for the `help` subcommand and lets you know when this happens;

2. you know which command has been selected by checking for the type of the leaf option structure that has been filled;

3. you can embed options within other options and mark them as `doc:"-"` to force the underlying parser to skip them (since they can't be parsed).

If you want to write a custom `"help"` command, you just need to pass to the toplevel Command call a subcommand implementing `"help"`. In which case, we will not register our internal interceptor for the `"help"` command.

Likewise, we'll attach to each subparser a -h/--help rule for printing help. But you can avoid this by adding an option that resolves to -h or --help.

func (*CommandParser) Getopt

func (p *CommandParser) Getopt(args []string) (*SelectedCommand, error)

Getopt parses command line arguments for the given command. This function returns either the selected command or subcommand, or an error.

See the documentation of the Subcommand factory for more usage information.

func (*CommandParser) MustGetopt

func (p *CommandParser) MustGetopt(args []string) *SelectedCommand

MustGetopt is exactly like Getopt except that it calls os.Exit(1) in case of error.

type Config

type Config interface {
	// contains filtered or unexported methods
}

Config is a piece of configuration for NewParser. You can pass a bunch of configs to NewParser to change its behavior.

func AtLeastOnePositionalArgument

func AtLeastOnePositionalArgument() Config

AtLeastOnePositionalArgument is a bit of config that causes Parse to fail if the user has provided no positional arguments.

func JustOnePositionalArgument

func JustOnePositionalArgument() Config

JustOnePositionalArgument is a bit of config that causes Parse to fail if the user has not provided exactly one positional argument.

func NoPositionalArguments

func NoPositionalArguments() Config

NoPositionalArguments is a bit of config that causes Parse to fail if the user has provided any positional argument.

func SetPositionalArgumentsPlaceholder

func SetPositionalArgumentsPlaceholder(name string) Config

SetPositionalArgumentsPlaceholder allows a user to set the name given to the positional arguments in the usage string.

Note that this value will not be printed if the parser does not accept at least a single positional argument.

If the provided name is empty, we will not modify the name that would printed by default for representing positional arguments.

func SetProgramName

func SetProgramName(name string) Config

SetProgramName sets the program name printed in the usage string.

If the provided name is empty, this option does not modify the program name that would be printed by default.

type Counter

type Counter int

Counter counts the number of times the given flag has been set from the command line.

func (*Counter) Set

func (b *Counter) Set(value string, opt getopt.Option) error

Set implements the Value.Set method.

func (*Counter) String

func (b *Counter) String() string

String implements the Value.String method.

type HasPrintedHelp

type HasPrintedHelp struct{}

HasPrintedHelp is the fake subcommand returned when CommandParser.Getopt or CommandParser.MustGetopt have printed an help message.

type Parser

type Parser interface {
	// Getopt parses the command line options in args, which in the
	// common case should be just os.Args.
	Getopt(args []string) error

	// MustGetopt is like Getopt but prints usage and exits on error.
	MustGetopt(args []string)

	// PrintUsage prints a detailed usage string on the given writer.
	PrintUsage(w io.Writer)

	// NArgs returns the number of positional arguments.
	NArgs() int

	// Args returns the positional arguments.
	Args() []string
}

Parser is a command line parser.

func MustNewParser

func MustNewParser(flags interface{}, configs ...Config) Parser

MustNewParser is like NewParser but print an error on stderr and calls os.Exit(1) if NewParser fails.

func NewParser

func NewParser(flags interface{}, configs ...Config) (Parser, error)

NewParser creates a new parser that stores the parsed options into the given `flags` opaque argument. Use `config...` to adjust the parser's behavior.

The `flags“ opaque argument must be a pointer to a struct where each field must be tagged with `doc:"..."`. We will use the value of the doc tag to generate the help message produced by PrintUsage. We will return an error if we find a field that is not documented. You should use `doc:"-"` to force the parser to skip a field.

The name of the structure field is converted to kebab case and used to generate the command line option.

To define a short option, add the `short:"v"` tag, where "v" must be a string containing a single one-byte character. If present, we'll use the value in short to determine the short option name.

The `required:"true"` tag indicates that an option is required.

For example:

type CLI struct {
  Help    bool   `doc:"prints this help message" short:"h"`
  Input   string `doc:"sets the URL to measure"  required:"true"`
  Verbose bool   `doc:"runs in verbose mode"     short:"v"`
}

becomes:

program [-h,--help] --input value [-v,--verbose]

Note that, by default, this parser does not treat `-h` or `--help` specially; you'll need to implement actions for them.

By default, the returned parser accepts any number of positional arguments, as the original getopt does. You can change that by using, e.g., the NoPositionalArguments Config constructor to force the parser to fail if the user has specified one or more positional arguments.

For example:

parser, err := getoptx.NewParser(&cli, getoptx.NoPositionalArguments())

Likewise, you can use SetProgramName and SetPositionalArguments to control exactly how PrintUsage works.

This constructor returns either a valid parser and a nil error, on success, or a nil parser and an error, on failure.

type SelectedCommand

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

SelectedCommand is the type returned by successful parsing of command line arguments by CommandParser.{Must,}Getopt.

See the documentation of Subcommand for more info on actual usage.

func (*SelectedCommand) Args

func (sc *SelectedCommand) Args() []string

Args returns the selected command's positional arguments.

func (*SelectedCommand) NArgs

func (sc *SelectedCommand) NArgs() int

NArgs returns the number of positional arguments.

func (*SelectedCommand) Options

func (sc *SelectedCommand) Options() interface{}

Options returns the selected command's options. See the documentation of Subcommand for more information on how to use this method.

Directories

Path Synopsis
example
complex command
simple command

Jump to

Keyboard shortcuts

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