getoptions

package module
v0.30.0 Latest Latest
Warning

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

Go to latest
Published: Feb 22, 2024 License: MPL-2.0 Imports: 17 Imported by: 56

README

= go-getoptions
David Gamba, https://github.com/DavidGamba
:idprefix:
:name: go-getoptions
:toc: macro
:toclevels: 4

Fully featured Go (golang) command line option parser with built-in auto-completion support.

image:https://pkg.go.dev/badge/github.com/DavidGamba/go-getoptions.svg["Go Reference", link="https://pkg.go.dev/github.com/DavidGamba/go-getoptions"]
image:https://github.com/DavidGamba/go-getoptions/actions/workflows/test.yml/badge.svg?branch=master["Build Status", link="https://github.com/DavidGamba/go-getoptions/actions/workflows/test.yml?query=branch:master"]
image:https://codecov.io/github/DavidGamba/go-getoptions/coverage.svg?branch=master["Coverage via Codecov", link="https://codecov.io/github/DavidGamba/go-getoptions?branch=release"]

Video Demo: https://youtu.be/1ZGyIkC5shM

toc::[]

[[quick_overview]]
== Quick overview

. See a detailed video demonstration: https://youtu.be/1ZGyIkC5shM

. Define your command line specification:
+
[source,go]
----
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/DavidGamba/go-getoptions"
)

var Logger = log.New(os.Stderr, "", log.LstdFlags)

func main() {
	os.Exit(program(os.Args))
}

func program(args []string) int {
	ctx, cancel, done := getoptions.InterruptContext()
	defer func() { cancel(); <-done }()

	opt := getoptions.New()
	opt.Self("myscript", "Simple demo script")
	opt.Bool("debug", false, opt.GetEnv("DEBUG"))
	opt.Int("greet", 0, opt.Required(), opt.Description("Number of times to greet."))
	opt.StringMap("list", 1, 99, opt.Description("Greeting list by language."))
	opt.Bool("quiet", false, opt.GetEnv("QUIET"))
	opt.HelpSynopsisArg("<name>", "Name to greet.")
	opt.SetCommandFn(Run)
	opt.HelpCommand("help", opt.Alias("?"))
	remaining, err := opt.Parse(args[1:])
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
		return 1
	}
	if opt.Called("quiet") {
		Logger.SetOutput(io.Discard)
	}

	err = opt.Dispatch(ctx, remaining)
	if err != nil {
		if errors.Is(err, getoptions.ErrorHelpCalled) {
			return 1
		}
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
		if errors.Is(err, getoptions.ErrorParsing) {
			fmt.Fprintf(os.Stderr, "\n"+opt.Help())
		}
		return 1
	}
	return 0
}

func Run(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
	// Get arguments and options
	name, _, err := opt.GetRequiredArg(args)
	if err != nil {
		return err
	}
	greetCount := opt.Value("greet").(int)
	list := opt.Value("list").(map[string]string)

	Logger.Printf("Running: %v", args)

	// Use the int variable
	for i := 0; i < greetCount; i++ {
		fmt.Printf("Hello %s, from go-getoptions!\n", name)
	}

	// Use the map[string]string variable
	if len(list) > 0 {
		fmt.Printf("Greeting List:\n")
		for k, v := range list {
			fmt.Printf("\t%s=%s\n", k, v)
		}
	}

	return nil
}
----

. Call it:
+
.Show help
----
$ ./myscript help
NAME:
    myscript - Simple demo script

SYNOPSIS:
    myscript --greet <int> [--debug] [--help|-?] [--list <key=value>...]...
             [--quiet] <name>

ARGUMENTS:
    <name>                   Name to greet.

REQUIRED PARAMETERS:
    --greet <int>            Number of times to greet.

OPTIONS:
    --debug                  (default: false, env: DEBUG)

    --help|-?                (default: false)

    --list <key=value>...    Greeting list by language. (default: {})

    --quiet                  (default: false, env: QUIET)
----
+
.Show errors
----
$ ./myscript
ERROR: Missing required parameter 'greet'
----
+
.Show errors
----
$ ./myscript -g
ERROR: Missing argument for option 'greet'!
----
+
.Show errors
----
$ ./myscript -g 3
ERROR: Missing <name>
SYNOPSIS:
    myscript --greet <int> [--debug] [--help|-?] [--list <key=value>...]...
             [--quiet] <name>
----
+
.Use of int option
----
$ ./myscript -g 3 David
2024/01/04 23:25:14 Running: [David]
Hello David, from go-getoptions!
Hello David, from go-getoptions!
Hello David, from go-getoptions!
----
+
.Use of bool option
----
$ ./myscript -g 1 David --quiet
Hello David, from go-getoptions!
----
+
.Use of map option
----
$ ./myscript -g 0 David -l en='Hello World' es='Hola Mundo'
2024/01/04 23:27:00 Running: [David]
Greeting List:
	en=Hello World
	es=Hola Mundo
----

NOTE: If you are starting a new project, instead of copying the example code from above, use the code from the link:./docs/new-project-templates.adoc[New Project Templates].

== Examples

=== Simple script

A simple script link:./examples/myscript/main.go[]

To use the autocompletion, cd to the link:./examples/myscript[] dir and run: `source sourceme.bash`
The run `go build` and `./myscript`.

Tab completion for this script is triggered for options only, so you need to have a dash `-` to trigger it: `./myscript -<tab><tab>`

=== Program with multiple commands separated in multiple packages

This is the other extreme, a large program that can separate each command in a separate go package.

The base is located at link:./examples/complex/main.go[]

The commands are located at:

* link:./examples/complex/greet/greet.go[]
* link:./examples/complex/log/log.go[]
* link:./examples/complex/show/show.go[]
* link:./examples/complex/slow/slow.go[]

To use the autocompletion, cd to the link:./examples/complex[] dir and run: `source sourceme.bash`
The run `go build` and `./complex`.

Tab completion without arguments triggers completion for commands, for option completion add a dash `-` and trigger it: `./complex -<tab><tab>`

The link:./examples/complex/slow/slow.go[slow] command shows an example of an slow command that can be cancelled with `Ctrl+C`.
The cancellation is passed to the command through `context.Context` and it is handled at the command to stop taking new work and trigger a cleanup routine.
Running `Ctrl+C` twice cancels the cancellation routine and fully cancels the program.

The link:./examples/complex/greet/greet.go[greet] command shows an example of using commands and subcommands.

=== Directed Acyclic Graph Build System

This example shows task dependency orchestration and parallelization link:./examples/dag/main.go[].

To use the autocompletion, cd to the link:./examples/dag[] dir and run: `source sourceme.bash`
The run `go build` and `./dag`.

Tab completion without arguments triggers completion for commands, for option completion add a dash `-` and trigger it: `./dag -<tab><tab>`

== DAG Build System

For an overview of the Directed Acyclic Graph Build System see link:./dag/README.adoc[]

NOTE: The DAG code is in a separate package so it is not pulled in by default.

== Features

• Built in auto completion.
A single line of bash is all it takes.
+
[source,bash]
----
complete -o default -C my-go-program my-go-program
----
+
Zshell is also supported, by exporting `ZSHELL=true` in your environment and using `bashcompinit`.

• Allow passing options and non-options (arguments) in any order.

• Support for `--long` options.

• Support for short (`-s`) options with flexible behaviour (see the <<operation_modes>> section for details):

  - Normal (default)
  - Bundling
  - SingleDash

• `Called()` method indicates if the option was passed on the command line.

• Multiple aliases for the same option. e.g. `help`, `man`.

• `CalledAs()` method indicates what alias was used to call the option on the command line.

• Synopsis and option list automated help.

• Boolean, String, Int, Float64, Slice and Map type options.

• Options with Array arguments.
The same option can be used multiple times with different arguments.
The list of arguments will be saved into an Slice.

• Options with array arguments and multiple entries.
+
For example, instead of writing:
`color --r 10 --g 20 --b 30 --next-option`
or
`color --rgb 10 --rgb 20 --rgb 30 --next-option`
the input could be:
`color --rgb 10 20 30 --next-option`

• When using integer array options with multiple arguments, positive integer ranges are allowed.
+
For example, Instead of writing:
`csv --columns 1 2 3`
or
`csv --columns 1 --columns 2 --columns 3`
The input could be:
`csv --columns 1..3`

• Options with Key Value arguments.
This allows the same option to be used multiple times with arguments of key value type.
+
For example: `rpmbuild --define name=myrpm --define version=123`

• Options with key value arguments and multiple entries.
+
For example, instead of writing:
`connection --server hostname=serverIP --server port=123 --client hostname=localhost --client port=456`
the input could be:
`connection --server hostname=serverIP port=123 --client hostname=localhost port=456`

• Supports command line options with '='.
+
For example: You can use `--string=mystring` and `--string mystring`.

• Allows passing arguments to options that start with dash `-` when passed after equal.
+
For example: `--string=--hello` and `--int=-123`.

• Supports passing `--` to stop parsing arguments (everything after will be left in the `remaining []string`).

• Options with optional arguments.
If the default argument is not passed the default is set.
+
For example: You can call `--int 123` which yields `123` or `--int` which yields the given default.

• Allows abbreviations when the provided option is not ambiguous.
+
For example: An option called `build` can be called with `--b`, `--bu`, `--bui`, `--buil` and `--build` as long as there is no ambiguity.
In the case of ambiguity, the shortest non ambiguous combination is required.

• Support for the lonesome dash "-".
To indicate, for example, when to read input from STDIO.

• Incremental options.
Allows the same option to be called multiple times to increment a counter.

• Supports case sensitive options.
For example, you can use `v` to define `verbose` and `V` to define `Version`.

• Support indicating if an option is required and allows overriding the default error message.

• Errors and Help Strings exposed as public variables to allow overriding them for internationalization.

• Supports program commands and subcommands (when a command is passed a command function is triggered to handle the command logic).

• Built in `opt.Dispatch` function calls commands and propagates context, options, arguments and cancellation signals.

• Multiple ways of managing unknown options:
  - Fail on unknown (default).
  - Warn on unknown.
  - Pass through, allows for commands and can be combined with Require Order.

• Require order: Allows for commands. Stop parsing arguments when the first non-option is found.
When mixed with Pass through, it also stops parsing arguments when the first unmatched option is found.

• Set options by reading Environment Variables.
Precedence is CLI option over Env Var over Default.

== How to install it

. Get it from github:
+
`go get github.com/DavidGamba/go-getoptions`

. Then import it:
+
`import "github.com/DavidGamba/go-getoptions" // As getoptions`

. Enjoy!

== Dependencies

Go 1.16+

Only the last two versions of Go will be supported.

== Introduction

NOTE: For a <<quick_overview>>, jump to that section in the TOC or review the http://godoc.org/github.com/DavidGamba/go-getoptions[GoDoc Documentation].

Option parsing is the act of taking command line (CLI) arguments and converting them into meaningful structures within the program.

First declare a `getoptions` instance:

[source, go]
----
opt := getoptions.New()
----

Then declare the options you want to parse:

[source, go]
----
opt.String("string", "default_value")
----

Optionally, define option modifiers:

[source, go]
----
opt.String("string", "default_value",

	opt.Alias("s"),                             // Allow -s as an alias for --string
	opt.Description("This is a string option"), // Add a description to the option
	opt.Required(),                             // Mark the option as required
	opt.GetEnv("STRING"),                       // Set the environment variable to read the option from
	opt.ArgName("mystring"),                    // Set the argument name for the help output
	                                            //   The help with show --string <mystring> instead of --string <string>
	opt.ValidValues("value1", "value2"),        // Set the valid values for the option, these are used for autocompletion too
	opt.SetCalled(true),                        // Forcefully set the option as if called in the CLI
)
----

You can also define arguments:

[source, go]
----
opt.HelpSynopsisArg("<arg1>", "arg1 description")
opt.HelpSynopsisArg("<arg2>", "arg2 description")
----

Define the function for the program:

----
opt.SetCommandFn(Run)
----

If no function is defined and `opt.Dispatch` is called, the program will show a help message with any commands or subcommands.

Define any commands and their options, arguments and functions:

[source, go]
----
cmd := opt.NewCommand("command", "command description")
cmd.String("int", 123)
cmd.HelpSynopsisArg("<arg1>", "arg1 description")
cmd.SetCommandFn(CommandRun)
----

NOTE: Options defined at a parent level will be inherited by the command unless `cmd.UnsetOptions()` is called.

After defining options and commands declare the help command, it must be the last one defined.

[source, go]
----
opt.HelpCommand("help", opt.Alias("?"))
----

Parse the CLI arguments (or any `[]string`):

[source, go]
----
remaining, err := opt.Parse(os.Args[1:])
----

Finally, call dispatch which will call the proper command function for the given arguments:

[source, go]
----
err = opt.Dispatch(ctx, remaining)
----

Dispatch requires a `context.Context` to be passed which can be used to propagate cancellation signals or configuration values.

A built in helper to create a context with cancellation support (`os.Interrupt`, `syscall.SIGHUP`, `syscall.SIGTERM`) is provided:

[source, go]
----
ctx, cancel, done := getoptions.InterruptContext()
defer func() { cancel(); <-done }()

err = opt.Dispatch(ctx, remaining)
----

The actual functions running the business logic are the `CommandFn` functions set with the `SetCommandFn`.

The `CommandFn` function signature is:

[source, go]
----
func Name(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
	return nil
}
----

This function will receive the context, the parsed options and the remaining arguments.

Read the received options from the `opt` variable.

[source, go]
----
func Name(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
	file := opt.Value("file").(string)
	count := opt.Value("count").(int)
	tags := opt.Value("tags").(map[string]string)

	// logic

	return nil
}
----

NOTE: The `opt.Value` function returns an `interface{}` so it needs to be type casted to the proper type.
The type cast will panic if trying to read an option that is not defined.

Read the received arguments from the `args` slice.
Additionally, use the `opt.GetRequiredArg` (with int and float64 variants) to simplify handling required arguments and providing error messages.

[source, go]
----
func Name(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
	arg1, args, err := opt.GetRequiredArgInt(args)
	if err != nil {
		return err
	}

	// logic

	return nil
}
----

== Automatically generate help

For a proper extended man page for your program consider link:http://asciidoctor.org/[asciidoctor] that can generate manpages written in the Asciidoc markup.

For the built-in help, you can add a description to your program:

- `opt.Self("", "This is a program description")`

NOTE: When the first argument is empty, it will use the program name from `os.Args[0]`.

For options help ensure you add option descriptions and argument names.

- `opt.Description("This is a string option")`
- `opt.ArgName("mystring")`

The help command needs to be defined after all options, commands and subcommands.

`opt.HelpCommand("help", opt.Alias("?"))`

When calling the help command, you get the full help.
Optionally you can print only given sections of the Help.

For example:

[source, go]
----
fmt.Fprintf(os.Stderr, "%s", opt.Help(getoptions.HelpSynopsis))
----

Or through a helper:

[source, go]
----
func ForceUnlock(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
	lockID, args, err := opt.GetRequiredArg(args)
	if err != nil {
		return err
	}
----

In the code above, if there is no argument passed, the `GetRequiredArg` will print an error plus the synopsis:

----
ERROR: Missing <lock-id>
SYNOPSIS:
    program [--help] <lock-id>
----

The error return is `getoptions.ErrorHelpCalled` which signals the help is already printed.
The dispatch error handling can handle this error and not print and additional error message.


[source, go]
----
	err = opt.Dispatch(ctx, remaining)
	if err != nil {
		if errors.Is(err, getoptions.ErrorHelpCalled) {
			return 1
		}
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
		if errors.Is(err, getoptions.ErrorParsing) {
			fmt.Fprintf(os.Stderr, "\n"+opt.Help())
		}
		return 1
	}
	return 0
----

Another helpful error to check for is `getoptions.ErrorParsing`, as shown above, which indicates there was a problem parsing the CLI arguments.
This can be used, to print the help only in cases where the user didn't enter valid CLI options or arguments.

The built in help shows default values and environment variables when available.

It separates _COMMANDS_, _ARGUMENTS_, _REQUIRED PARAMETERS_ and _OPTIONS_ into separate sections.

For example, the following is a script using the built in help:

----
$ bt terraform force-unlock help
NAME:
    bt terraform force-unlock

SYNOPSIS:
    bt terraform force-unlock [--help|-?] [--profile <string>] [--quiet]
                              [--ws <string>] <lock-id>

ARGUMENTS:
    <lock-id>             Lock ID

OPTIONS:
    --help|-?             (default: false)

    --profile <string>    BT Terraform Profile to use (default: "default", env: AWS_PROFILE)

    --quiet               (default: false, env: QUIET)

    --ws <string>         Workspace to use (default: "")
----

And below is the output of the automated help of a program with multiple commands:

----
$ tz help
SYNOPSIS:
    tz [--config|-c <string>] [--format-standard|--format-12-hour|--format-12h]
       [--group <string>] [--help|-?] [--short|-s] [--verbose] <command> [<args>]

COMMANDS:
    cities     filter cities list
    list       list all timezones
    version    show version

OPTIONS:
    --config|-c <string>                               Config file (default: "")

    --format-standard|--format-12-hour|--format-12h    Use standard 12 hour AM/PM time format (default: false)

    --group <string>                                   Group to show (default: "")

    --help|-?                                          (default: false)

    --short|-s                                         Don't show timezone bars (default: false)

    --verbose                                          Enable logging (default: false, env: TZ_VERBOSE)

Use 'tz help <command>' for extra details.
----

Any built-in string in `go-getoptions`, like titles, is exposed as a public variable so it can be overridden for internationalization.

== Autocompletion

To enable bash autocompletion, add the following line to your bash profile:

[source,bash]
----
complete -o default -C my-go-program my-go-program
----

For the above to work, the program must be in the PATH.
Otherwise:

[source,bash]
----
complete -o default -C "$HOME/go/bin/my-go-program" my-go-program
----

To enable zsh autocompletion, add the following line to your zsh profile:

[source,zsh]
----
export ZSHELL="true"
autoload -U +X compinit && compinit
autoload -U +X bashcompinit && bashcompinit
complete -o default -C my-go-program my-go-program
----

The `ZSHELL="true"` export is required because bash and zsh have different ways of handling autocompletion and there is no reliable way to detect which shell is being used.

If testing completion in the CLI, you might require to first clean the completion entry that `complete` auto generates when hitting `Tab` twice:

`complete -r ./my-go-program 2>/dev/null`

When providing these as scripts that users source but not add into their profile you can use the following `sourceme.bash` script:

.sourceme.bash
[source,bash]
----
#!/bin/bash

# Remove existing entries to ensure the right one is loaded
# This is not required when the completion one liner is loaded in your bashrc.
complete -r ./my-go-program 2>/dev/null

complete -o default -C "$PWD/my-go-program" my-go-program
----

Then when the users go into the directory and run `source sourceme.bash` the autocompletion will be enabled.

== Options

=== Boolean options

Opposite of default when passed on the command line.

- `ptr := opt.Bool(name, false)`
- `opt.BoolVar(&ptr, name, false)`
- Additionally, if all you want to know is if the option was passed you can use: `opt.Bool(name, false)` (without capturing its return value) and then check `opt.Called(name)`.
- Also, you can get the value with `v, ok := opt.Value(name).(bool)`.

For example:

`ls --all`

=== Options with String arguments

The option will accept a string argument.

- `ptr := opt.String(name, "default")`.
- `opt.StringVar(&ptr, name, "default")`.

For example:

`grepp --ignore .txt`

Additionally, arguments to options can be passed with the `=` symbol.

`grepp --ignore=.txt` or `count --from=-123`

=== Options with Integer arguments

Parse an option string argument into an Integer and provide an user error if the string provided is not an integer.

- `ptr := opt.Int(name, 0)`.
- `opt.IntVar(&ptr, name, 0)`.

For example:

`grepp --contex-lines 3`

and:

`grepp --context-lines string`

  Error: 'string' is not a valid integer.

=== Options with Floating point arguments

Parse an option string argument into a Floating point value and provide an user error if the string provided is not a valid floating point.

- `ptr := opt.Float64(name, 3.14)`.
- `opt.Float64Var(&ptr, name, 3.14)`.

For example:

`program --approximation 3.5`

and:

----
$ program --approximation string

Error: 'string' is not a valid floating point value.
----

=== Options with array arguments

This allows the same option to be used multiple times with different arguments.
The list of arguments will be saved into a Slice inside the program.

- `ptr := opt.StringSlice(name, 1, 99)`.
- `opt.StringSliceVar(&ptr, name, 1, 99)`.
- `ptr := opt.IntSlice(name, 1, 99)`.
- `opt.IntSliceVar(&ptr, name, 1, 99)`.
- `ptr := opt.Float64Slice(name, 1, 99)`.
- `opt.Float64SliceVar(&ptr, name, 1, 99)`.

For example:

`list-files --exclude .txt --exclude .html --exclude .pdf`

or:

`list-files --exclude .txt .html .pdf`

The setup for this feature should allow for the user to continue using both versions of the input, that is passing one argument at a time or passing the 3 arguments at once, or allow the setup to force the user to have to use the 3 arguments at once version.
This is accomplished with the minimum and maximum setup parameters.

The minimum setup parameter indicates the minimum amount of parameters the user can pass at a time.
For the example above, the parameter could be set to 3 to force the user to have to pass the 3 arguments at once.
When set to 1, the user will be able to pass a single parameter per option call.

The maximum setup parameter indicates the maximum amount of parameters the user can pass at a time.
The option parser will leave any non option argument after the maximum in the `remaining` slice.

Good defaults are `1` and `99`.

Additionally, in the case of integers, positive integer ranges are allowed.
For example:

Instead of writing: `csv --columns 1 2 3` or `csv --columns 1 --columns 2 --columns 3`

The input could be: `csv --columns 1..3`.

=== Options with Key Value arguments

This allows the same option to be used multiple times with arguments of key value type.

- `strMap := opt.StringMap(name, 1, 99)`.
- `opt.StringMapVar(&ptr, name, 1, 99)`.

For example:

`rpmbuild --define name=myrpm --define version=123`

or:

`rpmbuild --define name=myrpm version=123`

Also, instead of writing: `connection --server hostname=serverIP --server port=123 --client hostname=localhost --client port=456`

The input could be: `connection --server hostname=serverIP port=123 --client hostname=localhost port=456`

=== Incremental option

- `ptr := opt.Increment(name, default_value)`.
- `opt.IncrementVar(&ptr, name, default_value)`.

Some options can be passed more than once to increment an internal counter.
For example:

`command --v --v --v`

Could increase the verbosity level each time the option is passed.

=== Options with optional arguments

- `ptr := opt.StringOptional(name, default_value)`.
- `ptr := opt.IntOptional(name, default_value)`.
- `ptr := opt.Float64Optional(name, default_value)`.
- The above should be used in combination with `opt.Called(name)`.

With regular options, when the argument is not passed (for example: `--level` instead of `--level=debug`) you will get a _Missing argument_ error.
When using options with optional arguments, If the argument is not passed, the option will set the default value for the option type.
For this feature to be fully effective in strong typed languages where types have defaults, there must be a means to query the option parser to determine whether or not the option was called.

For example, for the following definition:

`ptr := opt.StringOptional("level", "info")`

* If the option `level` is called with just `--level`, the value of `*ptr` is the default `"info"` and querying `opt.Called("level")` returns `true`.
* If the option `level` is called with `--level=debug`, the value of `*ptr` is `"debug"` and querying `opt.Called("level")` returns `true`.
* Finally, If the option `level` is not called, the value of `*ptr` is the default `"info"` and querying `opt.Called("level")` returns `false`.

=== Stop parsing options when `--` is passed

Useful when arguments start with dash `-` and you don't want them interpreted as options.

=== Allow passing options and non-options in any order

Some option parsers force you to put the options before or after the arguments.
That is really annoying!

The `go-getoptions` parser knows when to expect arguments for an option so they can be intermixed with arguments without issues.

=== Allow pass through

- `opt.SetUnknownMode(getoptions.Pass)`.

Have an option to pass through unmatched options.
Useful when writing programs with multiple options depending on the main arguments.
The initial parser will only capture the help or global options and pass through everything else.
Additional argument parsing calls are invoked on the remaining arguments based on the initial input.

=== Fail on unknown

The opposite of the above option.
Useful if you want to ensure there are no input mistakes and force the application to stop.

In `go-getoptions` this is the default behaviour.

It can be explicitly set with:

`opt.SetUnknownMode(getoptions.Fail)`.

=== Warn on unknown

Less strict parsing of options.
This will warn the user that the option used is not a valid option but it will not stop the rest of the program.

In `go-getoptions` this is accomplished with:

- `opt.SetUnknownMode(getoptions.Warn)`.

=== Option Modifiers (ModifyFn)

==== Aliases

`opt.BoolVar(&flag, "flag", false, opt.Alias("alias", "alias-2"))`

Use `opt.CalledAs(<name>)` to determine the alias used to call the option.

==== Description

`opt.BoolVar(&flag, "flag", false, opt.Description("This is a flag"))`

Add a description to the option.

==== Required options

`opt.BoolVar(&flag, "flag", false, opt.Required())`

Mark an option as required.
Return an error if the option is not called.

Optionally, override the default error message with `opt.Required(msg)`.
For example:

`opt.BoolVar(&flag, "flag", false, opt.Required("Missing --flag!"))`

==== Read option value from environment variable

`opt.BoolVar(&flag, "flag", false, opt.GetEnv("FLAG"))`

Precedence is CLI option over Env Var over Default.

Supported for the following types:
- `opt.Bool` and `opt.BoolVar`
- `opt.String`, `opt.StringVar`, `opt.StringOptional`, and `opt.StringVarOptional`
- `opt.Int`, `opt.IntVar`, `opt.IntOptional`, and `opt.IntVarOptional`
- `opt.Float64`, `opt.Float64Var`, `opt.Float64Optional`, and `opt.Float64VarOptional`

NOTE: Non supported option types behave with a No-Op when `opt.GetEnv` is defined.

When using `opt.GetEnv` with `opt.Bool` or `opt.BoolVar`, only the words "true" or "false" are valid.
They can be provided in any casing, for example: "true", "True" or "TRUE".

NOTE: For numeric values, `opt.Int` and `opt.Float64` and their derivatives, environment variable string conversion errors are ignored and the default value is assigned.

==== Help argument name hint

`opt.StringVar(&str, "str", false, opt.ArgName("my_arg_name"))`

The default help string for an option is:

- string: "<string>"
- int: "<int>"
- float64: "<float64>"

Override it with `opt.ArgName("my_arg_name")`.
It additionally shows in the autocompletion hints.

==== Suggested values

`opt.StringVar(&str, "str", false, opt.SuggestedValues("value1", "value2"))`

This list will be added to the autocompletion engine.

==== Valid values

`opt.StringVar(&str, "str", false, opt.ValidValues("value1", "value2"))`

Limit the list of valid values for the option.
This list will be added to the autocompletion engine.

==== Set option as called

`opt.StringVar(&str, "str", false, opt.SetCalled(true))`

When calling `CommandFn` directly, it is sometimes useful to set the option as called.
Use cases are for testing and wrappers.

[[operation_modes]]
== Operation Modes: How to handle single dash '-' options

Notice how so far only long options (options starting with double dash `--`) have been mentioned.
There are 3 main ways to handle short options (options starting with only one dash `-`).

The behaviour for long options (options starting with double dash `--`) is consistent across operation modes.
The behaviour for short options (options starting with only one dash `-`) depends on the _operation mode_.
The sections below show the different operation modes.

=== Normal Mode (default)

|===
|Given argument |Interpretation

|--opt
a|option: `"opt"`,  argument: `nil`

|--opt=arg
a|option: `"opt"`, argument: `"arg"` footnote:[Argument gets type casted depending on option definition.]

|-opt
a|option: `"opt"`, argument: `nil`

|-opt=arg
a|option: `"opt"`, argument: `"arg"` footnote:[Argument gets type casted depending on option definition.]

|===

=== Bundling Mode

Set by defining `opt.SetMode(getoptions.Bundling)`.

|===
|Given option |Interpretation

|--opt
a|option: `"opt"`,  argument: `nil`

|--opt=arg
a|option: `"opt"`, argument: `"arg"` footnote:[Argument gets type casted depending on option definition.]

|-opt
a|option: `"o"`, argument: `nil` +
option: `"p"`, argument: `nil` +
option: `"t"`, argument: `nil`

|-opt=arg
a|option: `"o"`, argument: `nil` +
option: `"p"`, argument: `nil` +
option: `"t"`, argument: `"arg"` footnote:[Argument gets type casted depending on option definition.]

|===

=== Enforce Single Dash Mode

Set by defining `opt.SetMode(getoptions.SingleDash)`.

|===
|Given option |Interpretation

|--opt
a|option: `"opt"`,  argument: `nil`

|--opt=arg
a|option: `"opt"`, argument: `"arg"` footnote:[Argument gets type casted depending on option definition.]

|-opt
a|option: `"o"`, argument: `"pt"` footnote:[Argument gets type casted depending on option definition.]

|-opt=arg
a|option: `"o"`, argument: `"pt=arg"` footnote:[Argument gets type casted depending on option definition.]

|===

== Command behaviour

This section describes how the parser resolves ambiguities between the program and the command.

Given a definition like:

[source, go]
----
func main() {
	var profile, password string
	opt := New()
	opt.SetUnknownMode(Pass)
	opt.StringVar(&profile, "profile", "")
	command := NewCommand()
	command.StringVar(&password, "password", "")
	opt.Command(command.Self("command", "").SetCommandFn(commandFn))
	remaining, err := opt.Parse(os.Args[1:])
	...
	err = opt.Dispatch("help", remaining)
	...
}

func commandFn(opt *getoptions.GetOpt, args []string) error {
	args, err := opt.Parse(remaining)
	...
}
----

There is an option at the parent, `profile` and one at the command, `password`.
Passing `--p <arg>` is ambiguous and results in an error.
At minimum, `--pr <arg>` and `--pa <arg>` are required.

Given a definition like:

[source, go]
----
func main() {
	var profile, password string
	opt := New()
	opt.SetUnknownMode(Pass)
	opt.StringVar(&profile, "profile", "")
	command := NewCommand()
	command.StringVar(&password, "password", "", opt.Alias("p"))
	opt.Command(command.Self("command", "").SetCommandFn(commandFn))
	remaining, err := opt.Parse(os.Args[1:])
	...
	err = opt.Dispatch("help", remaining)
	...
}

func commandFn(opt *getoptions.GetOpt, args []string) error {
	args, err := opt.Parse(remaining)
	...
}
----

There is an option at the parent, `profile` and one at the command, `password` with alias `p`.
Passing `--p <arg>` at the parent results in the parent `opt.Parse` call to leave the `--p <arg>` option unhandled and leave it in the remaining slice.
The `opt.Dispatch` call gets the `-p <arg>` option and throws an error.
At minimum, `--pr <arg>` is required to call `profile` at the parent and command options must be passed after the command declaration.

For example, the calls below is correct:

	$ ./program -pr <profile> command -p <password>

	$ ./program command -pr <profile> -p <password>

But the following one is incorrect:

	./program -pr <profile> -p <password> command

[[roadmap]]
== ROADMAP

* Generate compilation errors for commands without a defined `CommandFn`.

* Create new error description for errors when parsing integer ranges (`1..3`).

* Case insensitive matching.

* prefix and prefix_pattern.
The string that starts options.
Defaults to "--" and "-" but could include "/" to support Win32 style argument handling.

* Allow grouping commands so they can have a different order other than alphabetical in the help output.

* Some Windows tests fail because the binary name includes .exe at the end.
Update test suite to accommodate for Windows.

* Introduce a `opt.NoArgs` so there are no `[<args>]` listed in the help output.

* Add `CustomCompletionFn` before release figure out how to have ways to have custom completions with different engines for arg1 and arg2.

* Figure out how to have custom completions for option values.

* Add OptionGroup to allow grouping options in the help output.

* Document CustomCompletion and ValidValues in autocompletion section.

* Mark optional as required in subcommand.

=== Possible Env Variable Roadmap

The Roadmap isn't clear given that there might not be enough value in implementing all of them.

* Handle `opt.Int` and `opt.Float64` errors.

StringSlice and StringSliceVar:: Comma separated? <- Most likely
+
Comma space separated?
Proper CSV parsing to allow comma escaping?

IntSlice and IntSliceVar:: Comma separated?

StringMap and StringMapVar:: Comma separated key=value?

== License

This file is part of go-getoptions.

Copyright (C) 2015-2024  David Gamba Rios

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.

Documentation

Overview

Package getoptions - Go option parser inspired on the flexibility of Perl’s GetOpt::Long.

It will operate on any given slice of strings and return the remaining (non used) command line arguments. This allows to easily subcommand.

See https://github.com/DavidGamba/go-getoptions for extra documentation details.

Features

• Allow passing options and non-options in any order.

• Support for `--long` options.

• Support for short (`-s`) options with flexible behaviour (see https://github.com/DavidGamba/go-getoptions#operation_modes for details):

  • Normal (default)
  • Bundling
  • SingleDash

• `Called()` method indicates if the option was passed on the command line.

• Multiple aliases for the same option. e.g. `help`, `man`.

• `CalledAs()` method indicates what alias was used to call the option on the command line.

• Simple synopsis and option list automated help.

• Boolean, String, Int and Float64 type options.

• Options with Array arguments. The same option can be used multiple times with different arguments. The list of arguments will be saved into an Array like structure inside the program.

• Options with array arguments and multiple entries. For example: `color --rgb 10 20 30 --next-option`

• When using integer array options with multiple arguments, positive integer ranges are allowed. For example: `1..3` to indicate `1 2 3`.

• Options with key value arguments and multiple entries.

• Options with Key Value arguments. This allows the same option to be used multiple times with arguments of key value type. For example: `rpmbuild --define name=myrpm --define version=123`.

• Supports passing `--` to stop parsing arguments (everything after will be left in the `remaining []string`).

• Supports command line options with '='. For example: You can use `--string=mystring` and `--string mystring`.

• Allows passing arguments to options that start with dash `-` when passed after equal. For example: `--string=--hello` and `--int=-123`.

• Options with optional arguments. If the default argument is not passed the default is set. For example: You can call `--int 123` which yields `123` or `--int` which yields the given default.

• Allows abbreviations when the provided option is not ambiguous. For example: An option called `build` can be called with `--b`, `--bu`, `--bui`, `--buil` and `--build` as long as there is no ambiguity. In the case of ambiguity, the shortest non ambiguous combination is required.

• Support for the lonesome dash "-". To indicate, for example, when to read input from STDIO.

• Incremental options. Allows the same option to be called multiple times to increment a counter.

• Supports case sensitive options. For example, you can use `v` to define `verbose` and `V` to define `Version`.

• Support indicating if an option is required and allows overriding default error message.

• Errors exposed as public variables to allow overriding them for internationalization.

• Supports subcommands (stop parsing arguments when non option is passed).

• Multiple ways of managing unknown options:

  • Fail on unknown (default).
  • Warn on unknown.
  • Pass through, allows for subcommands and can be combined with Require Order.

• Require order: Allows for subcommands. Stop parsing arguments when the first non-option is found. When mixed with Pass through, it also stops parsing arguments when the first unmatched option is found.

Panic

The library will panic if it finds that the programmer (not end user):

• Defined the same alias twice.

• Defined wrong min and max values for SliceMulti methods.

Example
package main

import (
	"fmt"
	"io"
	"log"
	"os"

	"github.com/DavidGamba/go-getoptions"
)

var logger = log.New(io.Discard, "DEBUG: ", log.LstdFlags)

func main() {
	// Declare the variables you want your options to update
	var debug bool
	var greetCount int
	var list map[string]string

	// Declare the GetOptions object
	opt := getoptions.New()

	// Options definition
	opt.Bool("help", false, opt.Alias("h", "?")) // Aliases can be defined
	opt.BoolVar(&debug, "debug", false)
	opt.IntVar(&greetCount, "greet", 0,
		opt.Required(),
		opt.Description("Number of times to greet."), // Set the automated help description
		opt.ArgName("number"),                        // Change the help synopsis arg from the default <int> to <number>
	)
	opt.StringMapVar(&list, "list", 1, 99,
		opt.Description("Greeting list by language."),
		opt.ArgName("lang=msg"), // Change the help synopsis arg from <key=value> to <lang=msg>
	)

	// // Parse cmdline arguments os.Args[1:]
	remaining, err := opt.Parse([]string{"-g", "2", "-l", "en='Hello World'", "es='Hola Mundo'"})

	// Handle help before handling user errors
	if opt.Called("help") {
		fmt.Fprint(os.Stderr, opt.Help())
		os.Exit(1)
	}

	// Handle user errors
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", err)
		fmt.Fprint(os.Stderr, opt.Help(getoptions.HelpSynopsis))
		os.Exit(1)
	}

	// Use the passed command line options... Enjoy!
	if debug {
		logger.SetOutput(os.Stderr)
	}
	logger.Printf("Unhandled CLI args: %v\n", remaining)

	// Use the int variable
	for i := 0; i < greetCount; i++ {
		fmt.Println("Hello World, from go-getoptions!")
	}

	// Use the map[string]string variable
	if len(list) > 0 {
		fmt.Printf("Greeting List:\n")
		for k, v := range list {
			fmt.Printf("\t%s=%s\n", k, v)
		}
	}

}
Output:

Hello World, from go-getoptions!
Hello World, from go-getoptions!
Greeting List:
	en='Hello World'
	es='Hola Mundo'

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrorHelpCalled = fmt.Errorf("help called")

ErrorHelpCalled - Indicates the help has been handled.

View Source
var ErrorNotFound = fmt.Errorf("not found")

ErrorNotFound - Generic not found error

View Source
var ErrorParsing = errors.New("")

ErrorParsing - Indicates that there was an error with cli args parsing

View Source
var Logger = log.New(io.Discard, "DEBUG: ", log.Ldate|log.Ltime|log.Lshortfile)

Logger instance set to `io.Discard` by default. Enable debug logging by setting: `Logger.SetOutput(os.Stderr)`.

View Source
var Writer io.Writer = os.Stderr // io.Writer to write warnings to. Defaults to os.Stderr.

Functions

func InterruptContext added in v0.25.0

func InterruptContext() (ctx context.Context, cancel context.CancelFunc, done chan struct{})

InterruptContext - Creates a top level context that listens to os.Interrupt, syscall.SIGHUP and syscall.SIGTERM and calls the CancelFunc if the signals are triggered. When the listener finishes its work, it sends a message to the done channel.

Use:

func main() { ...
ctx, cancel, done := getoptions.InterruptContext()
defer func() { cancel(); <-done }()

NOTE: InterruptContext is a method to reuse gopt.Writer

Types

type CommandFn added in v0.15.0

type CommandFn func(context.Context, *GetOpt, []string) error

CommandFn - Function signature for commands

type GetOpt

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

GetOpt - main object.

func New

func New() *GetOpt

New returns an empty object of type GetOpt. This is the starting point when using go-getoptions. For example:

opt := getoptions.New()

func (*GetOpt) Alias added in v0.12.0

func (gopt *GetOpt) Alias(alias ...string) ModifyFn

Alias - Adds aliases to an option.

Example
opt := getoptions.New()
opt.Bool("help", false, opt.Alias("?"))
_, _ = opt.Parse([]string{"-?"})

if opt.Called("help") {
	fmt.Println("help called")
}

if opt.CalledAs("help") == "?" {
	fmt.Println("help called as ?")
}
Output:

help called
help called as ?

func (*GetOpt) ArgName added in v0.13.0

func (gopt *GetOpt) ArgName(name string) ModifyFn

ArgName - Add an argument name to an option for use in automated help. For example, by default a string option will have a default synopsis as follows:

--host <string>

If ArgName("hostname") is used, the synopsis will read:

--host <hostname>
Example
opt := getoptions.New()
opt.Bool("help", false, opt.Alias("?"))
opt.String("host-default", "")
opt.String("host-custom", "", opt.ArgName("hostname"))
_, _ = opt.Parse([]string{"-?"})

if opt.Called("help") {
	fmt.Println(opt.Help())
}
Output:

SYNOPSIS:
    go-getoptions.test [--help|-?] [--host-custom <hostname>]
                       [--host-default <string>] [<args>]

OPTIONS:
    --help|-?                   (default: false)

    --host-custom <hostname>    (default: "")

    --host-default <string>     (default: "")

func (*GetOpt) Bool

func (gopt *GetOpt) Bool(name string, def bool, fns ...ModifyFn) *bool

Bool - define a `bool` option and its aliases. It returns a `*bool` pointing to the variable holding the result. If the option is found, the result will be the opposite of the provided default.

Example
opt := getoptions.New()
opt.Bool("help", false, opt.Alias("?"))
_, _ = opt.Parse([]string{"-?"})

if opt.Called("help") {
	fmt.Println(opt.Help())
}
Output:

SYNOPSIS:
    go-getoptions.test [--help|-?] [<args>]

OPTIONS:
    --help|-?    (default: false)

func (*GetOpt) BoolVar

func (gopt *GetOpt) BoolVar(p *bool, name string, def bool, fns ...ModifyFn)

BoolVar - define a `bool` option and its aliases. The result will be available through the variable marked by the given pointer. If the option is found, the result will be the opposite of the provided default.

Example
var help bool
opt := getoptions.New()
opt.BoolVar(&help, "help", false, opt.Alias("?"))
_, _ = opt.Parse([]string{"-?"})

if help {
	fmt.Println(opt.Help())
}
Output:

SYNOPSIS:
    go-getoptions.test [--help|-?] [<args>]

OPTIONS:
    --help|-?    (default: false)

func (*GetOpt) Called

func (gopt *GetOpt) Called(name string) bool

Called - Indicates if the option was passed on the command line. If the `name` is an option that wasn't declared it will return false.

Example
opt := getoptions.New()
opt.Bool("help", false, opt.Alias("?"))
_, _ = opt.Parse([]string{"-?"})

if opt.Called("help") {
	fmt.Println("help called")
}

if opt.CalledAs("help") == "?" {
	fmt.Println("help called as ?")
}
Output:

help called
help called as ?

func (*GetOpt) CalledAs added in v0.13.0

func (gopt *GetOpt) CalledAs(name string) string

CalledAs - Returns the alias used to call the option. Empty string otherwise.

If the `name` is an option that wasn't declared it will return an empty string.

For options that can be called multiple times, the last alias used is returned.

Example
opt := getoptions.New()
opt.Bool("help", false, opt.Alias("?"))
_, _ = opt.Parse([]string{"-?"})

if opt.Called("help") {
	fmt.Println("help called")
}

if opt.CalledAs("help") == "?" {
	fmt.Println("help called as ?")
}
Output:

help called
help called as ?

func (*GetOpt) CustomCompletion added in v0.14.0

func (gopt *GetOpt) CustomCompletion(list ...string) *GetOpt

CustomCompletion - Add a custom completion list.

func (*GetOpt) Description added in v0.13.0

func (gopt *GetOpt) Description(msg string) ModifyFn

Description - Add a description to an option for use in automated help.

Example
opt := getoptions.New()
opt.Bool("help", false, opt.Alias("?"), opt.Description("Show help."))
opt.String("hostname", "golang.org", opt.ArgName("host|IP"), opt.Description("Hostname to use."))
opt.String("user", "", opt.ArgName("user_id"), opt.Required(), opt.Description("User to login as."))
opt.HelpSynopsisArg("[<filename>]", "File with hostnames.")
_, _ = opt.Parse([]string{"-?"})

if opt.Called("help") {
	fmt.Println(opt.Help())
}
Output:

SYNOPSIS:
    go-getoptions.test --user <user_id> [--help|-?] [--hostname <host|IP>]
                       [<filename>]

ARGUMENTS:
    [<filename>]            File with hostnames.

REQUIRED PARAMETERS:
    --user <user_id>        User to login as.

OPTIONS:
    --help|-?               Show help. (default: false)

    --hostname <host|IP>    Hostname to use. (default: "golang.org")

func (*GetOpt) Dispatch added in v0.15.0

func (gopt *GetOpt) Dispatch(ctx context.Context, remaining []string) error

Dispatch - Handles calling commands and subcommands after the call to Parse.

Example
runFn := func(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
	fmt.Println("a, b, c, d")
	return nil
}

opt := getoptions.New()
opt.NewCommand("list", "list stuff").SetCommandFn(runFn)
opt.HelpCommand("help", opt.Alias("?"))
remaining, err := opt.Parse([]string{"list"}) // <- argv set to call command
if err != nil {
	fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
	os.Exit(1)
}

getoptions.Writer = os.Stdout // Print help to stdout instead of stderr for test purpose

err = opt.Dispatch(context.Background(), remaining)
if err != nil {
	if errors.Is(err, getoptions.ErrorHelpCalled) {
		os.Exit(1)
	}
	fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
	if errors.Is(err, getoptions.ErrorParsing) {
		fmt.Fprintf(os.Stderr, "\n"+opt.Help())
	}
	os.Exit(1)
}
Output:

a, b, c, d
Example (BHelp)
runFn := func(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
	return nil
}

opt := getoptions.New()
opt.Bool("debug", false)
opt.NewCommand("list", "list stuff").SetCommandFn(runFn)
opt.HelpCommand("help", opt.Alias("?"), opt.Description("Show this help"))
remaining, err := opt.Parse([]string{"help"}) // <- argv set to call help
if err != nil {
	fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
	os.Exit(1)
}

getoptions.Writer = os.Stdout // Print help to stdout instead of stderr for test purpose

err = opt.Dispatch(context.Background(), remaining)
if err != nil {
	if !errors.Is(err, getoptions.ErrorHelpCalled) {
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
		os.Exit(1)
	}
}
Output:

SYNOPSIS:
    go-getoptions.test [--debug] [--help|-?] <command> [<args>]

COMMANDS:
    list    list stuff

OPTIONS:
    --debug      (default: false)

    --help|-?    Show this help (default: false)

Use 'go-getoptions.test help <command>' for extra details.
Example (CCommandHelp)
runFn := func(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
	return nil
}

opt := getoptions.New()
opt.Bool("debug", false)
list := opt.NewCommand("list", "list stuff").SetCommandFn(runFn)
list.Bool("list-opt", false)
opt.HelpCommand("help", opt.Alias("?"))
remaining, err := opt.Parse([]string{"help", "list"}) // <- argv set to call command help
if err != nil {
	fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
	os.Exit(1)
}

getoptions.Writer = os.Stdout // Print help to stdout instead of stderr for test purpose

err = opt.Dispatch(context.Background(), remaining)
if err != nil {
	if !errors.Is(err, getoptions.ErrorHelpCalled) {
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
		os.Exit(1)
	}
}
Output:

NAME:
    go-getoptions.test list - list stuff

SYNOPSIS:
    go-getoptions.test list [--debug] [--help|-?] [--list-opt] [<args>]

OPTIONS:
    --debug       (default: false)

    --help|-?     (default: false)

    --list-opt    (default: false)

func (*GetOpt) Float64

func (gopt *GetOpt) Float64(name string, def float64, fns ...ModifyFn) *float64

Float64 - define an `float64` option and its aliases.

func (*GetOpt) Float64Optional added in v0.23.0

func (gopt *GetOpt) Float64Optional(name string, def float64, fns ...ModifyFn) *float64

Float64Optional - define an `float64` option and its aliases.

func (*GetOpt) Float64Slice added in v0.25.0

func (gopt *GetOpt) Float64Slice(name string, min, max int, fns ...ModifyFn) *[]float64

func (*GetOpt) Float64SliceVar added in v0.25.0

func (gopt *GetOpt) Float64SliceVar(p *[]float64, name string, min, max int, fns ...ModifyFn)

func (*GetOpt) Float64Var

func (gopt *GetOpt) Float64Var(p *float64, name string, def float64, fns ...ModifyFn)

Float64Var - define an `float64` option and its aliases. The result will be available through the variable marked by the given pointer.

func (*GetOpt) Float64VarOptional added in v0.23.0

func (gopt *GetOpt) Float64VarOptional(p *float64, name string, def float64, fns ...ModifyFn)

Float64VarOptional - define an `float64` option and its aliases. The result will be available through the variable marked by the given pointer.

func (*GetOpt) GetEnv added in v0.18.0

func (gopt *GetOpt) GetEnv(name string) ModifyFn

GetEnv - Will read an environment variable if set. Precedence higher to lower: CLI option, environment variable, option default.

Currently, only `opt.Bool`, `opt.BoolVar`, `opt.String`, and `opt.StringVar` are supported.

When an environment variable that matches the variable from opt.GetEnv is set, opt.GetEnv will set opt.Called(name) to true and will set opt.CalledAs(name) to the name of the environment variable used. In other words, when an option is required (opt.Required is set) opt.GetEnv satisfies that requirement.

When using `opt.GetEnv` with `opt.Bool` or `opt.BoolVar`, only the words "true" or "false" are valid. They can be provided in any casing, for example: "true", "True" or "TRUE".

NOTE: Non supported option types behave with a No-Op when `opt.GetEnv` is defined.

Example
os.Setenv("_AWS_PROFILE", "production")

var profile string
opt := getoptions.New()
opt.StringVar(&profile, "profile", "default", opt.GetEnv("_AWS_PROFILE"))
_, _ = opt.Parse([]string{})

fmt.Println(profile)
os.Unsetenv("_AWS_PROFILE")
Output:

production

func (*GetOpt) GetRequiredArg added in v0.30.0

func (gopt *GetOpt) GetRequiredArg(args []string, sections ...HelpSection) (string, []string, error)

GetRequiredArg - Get the next argument from the args list and error if it doesn't exist. By default the error will include the HelpSynopsis section but it can be overriden with the list of sections or getoptions.HelpNone.

If the arguments have been named with `opt.HelpSynopsisArg` then the error will include the argument name.

func (*GetOpt) GetRequiredArgFloat64 added in v0.30.0

func (gopt *GetOpt) GetRequiredArgFloat64(args []string, sections ...HelpSection) (float64, []string, error)

Same as GetRequiredArg but converts the argument to a float64.

func (*GetOpt) GetRequiredArgInt added in v0.30.0

func (gopt *GetOpt) GetRequiredArgInt(args []string, sections ...HelpSection) (int, []string, error)

Same as GetRequiredArg but converts the argument to an int.

func (*GetOpt) Help added in v0.13.0

func (gopt *GetOpt) Help(sections ...HelpSection) string

Help - Default help string that is composed of all available sections.

func (*GetOpt) HelpCommand added in v0.15.0

func (gopt *GetOpt) HelpCommand(name string, fns ...ModifyFn)

HelpCommand - Declares a help command and a help option. Additionally, it allows to define aliases to the help option.

For example:

opt.HelpCommand("help", opt.Description("show this help"), opt.Alias("?"))

NOTE: Define after all other commands have been defined.

func (*GetOpt) HelpSynopsisArg added in v0.27.0

func (gopt *GetOpt) HelpSynopsisArg(arg, description string) *GetOpt

HelpSynopsisArg - Defines the help synopsis args description. Defaults to: [<args>]

func (*GetOpt) Increment

func (gopt *GetOpt) Increment(name string, def int, fns ...ModifyFn) *int

Increment - When called multiple times it increments the int counter defined by this option.

func (*GetOpt) IncrementVar

func (gopt *GetOpt) IncrementVar(p *int, name string, def int, fns ...ModifyFn)

IncrementVar - When called multiple times it increments the provided int.

func (*GetOpt) Int

func (gopt *GetOpt) Int(name string, def int, fns ...ModifyFn) *int

Int - define an `int` option and its aliases.

func (*GetOpt) IntOptional

func (gopt *GetOpt) IntOptional(name string, def int, fns ...ModifyFn) *int

IntOptional - define a `int` option and its aliases.

IntOptional will set the int to the provided default value when no value is given. For example, when called with `--intOpt 123`, the value is `123`. when called with `--intOpt` the value is the given default.

func (*GetOpt) IntSlice added in v0.11.0

func (gopt *GetOpt) IntSlice(name string, min, max int, fns ...ModifyFn) *[]int

IntSlice - define a `[]int` option and its aliases.

IntSlice will accept multiple calls to the same option and append them to the `[]int`. For example, when called with `--intRpt 1 --intRpt 2`, the value is `[]int{1, 2}`.

Additionally, IntSlice will allow to define a min and max amount of arguments to be passed at once. For example, when min is 1 and max is 3 and called with `--strRpt 1 2 3`, the value is `[]int{1, 2, 3}`. It could also be called with `--strRpt 1 --strRpt 2 --strRpt 3` for the same result.

When min is bigger than 1, it is required to pass the amount of arguments defined by min at once. For example: with `min = 2`, you at least require `--strRpt 1 2 --strRpt 3`

Finally, positive integer ranges are allowed. For example, Instead of writing: `csv --columns 1 2 3` or `csv --columns 1 --columns 2 --columns 3` The input could be: `csv --columns 1..3`.

func (*GetOpt) IntSliceVar added in v0.11.0

func (gopt *GetOpt) IntSliceVar(p *[]int, name string, min, max int, fns ...ModifyFn)

IntSliceVar - define a `[]int` option and its aliases.

IntSliceVar will accept multiple calls to the same option and append them to the `[]int`. For example, when called with `--intRpt 1 --intRpt 2`, the value is `[]int{1, 2}`.

Additionally, IntSliceVar will allow to define a min and max amount of arguments to be passed at once. For example, when min is 1 and max is 3 and called with `--strRpt 1 2 3`, the value is `[]int{1, 2, 3}`. It could also be called with `--strRpt 1 --strRpt 2 --strRpt 3` for the same result.

When min is bigger than 1, it is required to pass the amount of arguments defined by min at once. For example: with `min = 2`, you at least require `--strRpt 1 2 --strRpt 3`

Finally, positive integer ranges are allowed. For example, Instead of writing: `csv --columns 1 2 3` or `csv --columns 1 --columns 2 --columns 3` The input could be: `csv --columns 1..3`.

func (*GetOpt) IntVar

func (gopt *GetOpt) IntVar(p *int, name string, def int, fns ...ModifyFn)

IntVar - define an `int` option and its aliases. The result will be available through the variable marked by the given pointer.

func (*GetOpt) IntVarOptional

func (gopt *GetOpt) IntVarOptional(p *int, name string, def int, fns ...ModifyFn)

IntVarOptional - define a `int` option and its aliases. The result will be available through the variable marked by the given pointer.

IntOptional will set the int to the provided default value when no value is given. For example, when called with `--intOpt 123`, the value is `123`. when called with `--intOpt` the value is the given default.

func (*GetOpt) NewCommand added in v0.17.0

func (gopt *GetOpt) NewCommand(name string, description string) *GetOpt

NewCommand - Returns a new GetOpt object representing a new command.

NOTE: commands must be declared after all options are declared.

func (*GetOpt) Parse

func (gopt *GetOpt) Parse(args []string) ([]string, error)

Parse - Call the parse method when done describing. It will operate on any given slice of strings and return the remaining (non used) command line arguments. This allows to easily subcommand.

Parsing style is controlled by the `Set` methods (SetMode, SetRequireOrder, etc).

// Declare the GetOptions object
opt := getoptions.New()
...
// Parse cmdline arguments or any provided []string
remaining, err := opt.Parse(os.Args[1:])

func (*GetOpt) Required added in v0.12.0

func (gopt *GetOpt) Required(msg ...string) ModifyFn

Required - Automatically return an error if the option is not called. Optionally provide a custom error message, a default error message will be used otherwise.

func (*GetOpt) Self added in v0.14.0

func (gopt *GetOpt) Self(name string, description string) *GetOpt

Self - Set a custom name and description that will show in the automated help. If name is an empty string, it will only use the description and use the name as the executable name.

func (*GetOpt) SetCalled added in v0.29.0

func (gopt *GetOpt) SetCalled(called bool) ModifyFn

SetCalled - Mark the option as called using the option name. Useful when adding options to a CommandFn call from a wrapper function.

func (*GetOpt) SetCommandFn added in v0.15.0

func (gopt *GetOpt) SetCommandFn(fn CommandFn) *GetOpt

SetCommandFn - Defines the command entry point function.

func (*GetOpt) SetMapKeysToLower added in v0.11.0

func (gopt *GetOpt) SetMapKeysToLower() *GetOpt

SetMapKeysToLower - StringMap keys captured from StringMap are lower case. For example:

command --opt key=value

And:

command --opt KEY=value

Would both return `map[string]string{"key":"value"}`.

func (*GetOpt) SetMode

func (gopt *GetOpt) SetMode(mode Mode) *GetOpt

SetMode - Sets the Operation Mode. The operation mode only affects options starting with a single dash '-'. The available operation modes are: normal, bundling or singleDash.

The following table shows the different operation modes given the string "-opt=arg".

.Operation Modes for string "-opt=arg"
|===
|Mode             |Description

|normal           |option: opt
                    argument: arg

|bundling         |option: o
                    argument: nil
                   option: p
                    argument: nil
                   option: t
                    argument: arg

|singleDash       |option: o
                    argument: pt=arg

|===

See https://github.com/DavidGamba/go-getoptions#operation_modes for more details.

func (*GetOpt) SetRequireOrder

func (gopt *GetOpt) SetRequireOrder() *GetOpt

SetRequireOrder - Stop parsing options when an unknown entry is found. Put every remaining argument, including the unknown entry, in the `remaining` slice.

This is helpful when doing wrappers to other tools and you want to pass all options and arguments to that wrapper without relying on '--'.

An unknown entry is assumed to be the first argument that is not a known option or an argument to an option. When a subcommand is found, stop parsing arguments and let a subcommand handler handle the remaining arguments. For example:

program --opt arg unknown-command --subopt subarg

In the example above, `--opt` is an option and `arg` is an argument to an option, making `unknown-command` the first non option argument.

This method is useful when both the program and the unknown-command have option handlers for the same option.

For example, with:

program --help

`--help` is handled by `program`, and with:

program unknown-command --help

`--help` is not handled by `program` since there was a unknown-command that caused the parsing to stop. In this case, the `remaining` slice will contain `['unknown-command', '--help']` and that can be send to the wrapper handling code.

NOTE: In cases when the wrapper is written as a command use `opt.UnsetOptions` instead.

func (*GetOpt) SetUnknownMode

func (gopt *GetOpt) SetUnknownMode(mode UnknownMode) *GetOpt

SetUnknownMode - Determines how to behave when encountering an unknown option.

• 'fail' (default) will make 'Parse' return an error with the unknown option information.

• 'warn' will make 'Parse' print a user warning indicating there was an unknown option. The unknown option will be left in the remaining array.

• 'pass' will make 'Parse' ignore any unknown options and they will be passed onto the 'remaining' slice. This allows for subcommands. TODO: Add aliases

func (*GetOpt) SetValue added in v0.30.0

func (gopt *GetOpt) SetValue(name string, value ...string) error

SetValue - Set the value of the given option using strings as an argument.

Examples:

opt.SetValue("bool") // boolean - sets to opposite of default
opt.SetValue("int", "123") // int
err := opt.SetValue("float64", "x") // error because "x" is not a valid float64
opt.SetValue("slice", "a", "b", "c") // []string
opt.SetValue("slice", "d", "e", "f") // Can be called multiple times for options that allow it
opt.SetValue("map", "hello=world", "hola=mundo") // map[string]string

func (*GetOpt) String

func (gopt *GetOpt) String(name, def string, fns ...ModifyFn) *string

String - define a `string` option and its aliases. If not called, the return value will be that of the given default `def`.

func (*GetOpt) StringMap

func (gopt *GetOpt) StringMap(name string, min, max int, fns ...ModifyFn) map[string]string

StringMap - define a `map[string]string` option and its aliases.

StringMap will accept multiple calls of `key=value` type to the same option and add them to the `map[string]string` result. For example, when called with `--strMap k=v --strMap k2=v2`, the value is `map[string]string{"k":"v", "k2": "v2"}`.

Additionally, StringMap will allow to define a min and max amount of arguments to be passed at once. For example, when min is 1 and max is 3 and called with `--strMap k=v k2=v2 k3=v3`, the value is `map[string]string{"k":"v", "k2": "v2", "k3": "v3"}`. It could also be called with `--strMap k=v --strMap k2=v2 --strMap k3=v3` for the same result.

When min is bigger than 1, it is required to pass the amount of arguments defined by min at once. For example: with `min = 2`, you at least require `--strMap k=v k2=v2 --strMap k3=v3`

func (*GetOpt) StringMapVar added in v0.14.0

func (gopt *GetOpt) StringMapVar(m *map[string]string, name string, min, max int, fns ...ModifyFn)

StringMapVar - define a `map[string]string` option and its aliases.

StringMapVar will accept multiple calls of `key=value` type to the same option and add them to the `map[string]string` result. For example, when called with `--strMap k=v --strMap k2=v2`, the value is `map[string]string{"k":"v", "k2": "v2"}`.

Additionally, StringMapVar will allow to define a min and max amount of arguments to be passed at once. For example, when min is 1 and max is 3 and called with `--strMap k=v k2=v2 k3=v3`, the value is `map[string]string{"k":"v", "k2": "v2", "k3": "v3"}`. It could also be called with `--strMap k=v --strMap k2=v2 --strMap k3=v3` for the same result.

When min is bigger than 1, it is required to pass the amount of arguments defined by min at once. For example: with `min = 2`, you at least require `--strMap k=v k2=v2 --strMap k3=v3`

func (*GetOpt) StringOptional

func (gopt *GetOpt) StringOptional(name, def string, fns ...ModifyFn) *string

StringOptional - define a `string` option and its aliases.

StringOptional will set the string to the provided default value when no value is given. For example, when called with `--strOpt value`, the value is `value`. when called with `--strOpt` the value is the given default.

func (*GetOpt) StringSlice

func (gopt *GetOpt) StringSlice(name string, min, max int, fns ...ModifyFn) *[]string

StringSlice - define a `[]string` option and its aliases.

StringSlice will accept multiple calls to the same option and append them to the `[]string`. For example, when called with `--strRpt 1 --strRpt 2`, the value is `[]string{"1", "2"}`.

Additionally, StringSlice will allow to define a min and max amount of arguments to be passed at once. For example, when min is 1 and max is 3 and called with `--strRpt 1 2 3`, the value is `[]string{"1", "2", "3"}`. It could also be called with `--strRpt 1 --strRpt 2 --strRpt 3` for the same result.

When min is bigger than 1, it is required to pass the amount of arguments defined by min at once. For example: with `min = 2`, you at least require `--strRpt 1 2 --strRpt 3`

func (*GetOpt) StringSliceVar added in v0.11.0

func (gopt *GetOpt) StringSliceVar(p *[]string, name string, min, max int, fns ...ModifyFn)

StringSliceVar - define a `[]string` option and its aliases.

StringSliceVar will accept multiple calls to the same option and append them to the `[]string`. For example, when called with `--strRpt 1 --strRpt 2`, the value is `[]string{"1", "2"}`.

Additionally, StringSliceVar will allow to define a min and max amount of arguments to be passed at once. For example, when min is 1 and max is 3 and called with `--strRpt 1 2 3`, the value is `[]string{"1", "2", "3"}`. It could also be called with `--strRpt 1 --strRpt 2 --strRpt 3` for the same result.

When min is bigger than 1, it is required to pass the amount of arguments defined by min at once. For example: with `min = 2`, you at least require `--strRpt 1 2 --strRpt 3`

func (*GetOpt) StringVar

func (gopt *GetOpt) StringVar(p *string, name, def string, fns ...ModifyFn)

StringVar - define a `string` option and its aliases. The result will be available through the variable marked by the given pointer. If not called, the return value will be that of the given default `def`.

func (*GetOpt) StringVarOptional

func (gopt *GetOpt) StringVarOptional(p *string, name, def string, fns ...ModifyFn)

StringVarOptional - define a `string` option and its aliases. The result will be available through the variable marked by the given pointer.

StringVarOptional will set the string to the provided default value when no value is given. For example, when called with `--strOpt value`, the value is `value`. when called with `--strOpt` the value is the given default.

func (*GetOpt) SuggestedValues added in v0.30.0

func (gopt *GetOpt) SuggestedValues(values ...string) ModifyFn

SuggestedValues - adds a list of suggestions to the autocompletion for the option.

func (*GetOpt) UnsetOptions added in v0.25.0

func (gopt *GetOpt) UnsetOptions() *GetOpt

UnsetOptions - Unsets inherited options from parent program and parent commands. This is useful when writing wrappers around other commands.

NOTE: Use in combination with `opt.SetUnknownMode(getoptions.Pass)`

func (*GetOpt) ValidValues added in v0.25.0

func (gopt *GetOpt) ValidValues(values ...string) ModifyFn

ValidValues - adds a list of enforced valid values for the option. These are also added to the autocompletion engine.

func (*GetOpt) Value added in v0.14.0

func (gopt *GetOpt) Value(name string) interface{}

Value - Returns the value of the given option.

Type assertions are required in cases where the compiler can't determine the type by context. For example: `opt.Value("flag").(bool)`.

type HelpSection added in v0.14.0

type HelpSection int

HelpSection - Indicates what portion of the help to return.

const (
	HelpNone HelpSection = iota

	HelpName
	HelpSynopsis
	HelpCommandList
	HelpOptionList
	HelpCommandInfo
)

Help Output Types

type Mode added in v0.14.0

type Mode int

Mode - Operation mode for short options

const (
	Normal Mode = iota
	Bundling
	SingleDash
)

Operation modes

type ModifyFn added in v0.12.0

type ModifyFn func(parent *GetOpt, option *option.Option)

ModifyFn - Function signature for functions that modify an option.

type UnknownMode added in v0.14.0

type UnknownMode int

UnknownMode - Unknown option mode

const (
	Fail UnknownMode = iota
	Warn
	Pass
)

Unknown option modes - Action taken when an unknown option is encountered.

Directories

Path Synopsis
Package dag - Lightweight Directed Acyclic Graph (DAG) Build System.
Package dag - Lightweight Directed Acyclic Graph (DAG) Build System.
docs
examples
dag
internal
completion
Package completion - provides a Tree structure that can be used to define a program's completions.
Package completion - provides a Tree structure that can be used to define a program's completions.
help
Package help - internal help handling code.
Package help - internal help handling code.
option
Package option - internal option struct and methods.
Package option - internal option struct and methods.
sliceiterator
Package sliceiterator - builds an iterator from a slice to allow peaking for the next value.
Package sliceiterator - builds an iterator from a slice to allow peaking for the next value.
Package text - User facing strings.
Package text - User facing strings.

Jump to

Keyboard shortcuts

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