Skip to content

Latest commit

 

History

73 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Expressive flags for Go

Go Reference CI

Package xflags implements command-line flag parsing and is a compatible alternative to Go's flag package. This package provides higher-order features such as subcommands, positional arguments, required arguments, validation, support for environment variables and others.

Package xflags aims to make composing large, full-featured command line tools as simple and clean as possible. Chained setters are employed to configure commands and flags declaratively. There are no dependencies beyond the standard library, and no code generation or struct tags.

Install

go get github.com/cavaliergopher/xflags

Usage

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/cavaliergopher/xflags"
)

var flagName string

var App = xflags.NewCommand("greet", "Print a greeting").
	Flags(
		xflags.String(&flagName, "name", "World", "Who to greet"),
	).
	HandleFunc(func(ctx context.Context, inv *xflags.Invocation) error {
		fmt.Fprintf(inv.Stdout, "Hello, %s!\n", flagName)
		return nil
	})

func main() {
	ctx, stop := xflags.NotifyContext(context.Background())
	defer stop()
	os.Exit(xflags.Run(ctx, App))
}

Flag values are stored in variables you own, so they are read directly with no lookup by name. Configuration errors — a duplicate flag, a positional argument declared alongside subcommands — are reported when the command line is parsed.

A handler returns an error and Run turns it into an exit code: 0 for success or --help, 1 for a handler that failed, 2 for a command line that was wrong. An error may name its own code by implementing ExitCoder. The context comes from NotifyContext, which cancels it on the first interrupt and restores default signal handling so a second one kills a wedged process.

The Invocation tells the handler how it was called — which command ran, the path it was reached by, and anything after a -- terminator. A command is usually mounted by whoever composes the binary rather than by the team that wrote it, so its own path is not something it can know until it runs.

Command line syntax

The dialect is the POSIX Utility Syntax Guidelines plus GNU long options — what getopt_long accepts — rather than Go's flag package, where one dash and two mean the same thing.

-f            --flag           a flag taking no value
-f=false      --flag=false     a boolean set false
-fx  -f=x     --flag=x         a value attached to its flag
-f x          --flag x         a value in the next argument
-abc                           -a -b -c, while each takes no value
-abfx                          -a -b -f x, where -f takes one

Two arguments are not flags at all. A bare - is an ordinary operand, left to the handler to interpret, and -- ends option processing for commands that set WithTerminator.

An argument beginning with - is never taken as a detached value, so --count -5 is a missing value rather than negative five; write --count=-5. Flags may appear among the operands in any order, and a flag is legal from the point its own command is named onward.

Four departures from getopt_long are deliberate, and the ADR argues each one:

  • Attached values follow Go, not getopt. -n=value sets value rather than =value, and a boolean accepts an attached value, so --flag=false and -f=false both set false. Without it a boolean could not be turned off at all.
  • Long options may not be abbreviated. getopt_long accepts any unique prefix, but a command tree makes "unique" a moving target: adding a flag to a subcommand can break a script that never changed.
  • -- is opt-in, via WithTerminator, and what follows it reaches the handler as Invocation.Args rather than binding to operand slots. POSIX has no subcommands, so it has no case to forward arguments to.
  • -h and --help are reserved by the parser, which is GNU practice rather than POSIX.

Describing a command

Command.Describe compiles a command tree into the plain data types in the desc package: every command, flag group and flag, with behavior dropped and ancestry resolved. The default help formatter walks it, and so can your own tooling.

See the docs for comprehensive examples.

About

Expressive flags for Go

Topics

Resources

Stars

18 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages