commandeer

package module
Version: v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2019 License: MIT Imports: 9 Imported by: 10

README

Commandeer

Go Report Card GoDoc Coverage

Image

Commandeer sets up command line flags based on struct fields and tags.

Do you...

  • like to develop Go apps as libraries with tiny main packages?
  • get frustrated keeping your flags up to date as your code evolves?
  • feel irked by the overlap between comments on struct fields and help strings for flags?
  • hate switching between your app's main and library packages?

You might like Commandeer. See the godoc for detailed usage, or just...

Try It!

Here's how it works, define your app like so:

package myapp

import "fmt"

type Main struct {
	Num     int    `help:"How many does it take?"`
	Vehicle string `help:"What did they get?"`
}

func NewMain() *Main { return &Main{Num: 5, Vehicle: "jeep"} }

func (m *Main) Run() error {
	if m.Num < 2 || m.Vehicle == "" {
		return fmt.Errorf("Need more gophers and/or vehicles.")
	}
	fmt.Printf("%d gophers stole my %s!\n", m.Num, m.Vehicle)
	return nil
}

and your main package:

package main

import (
	"fmt"

	"github.com/jaffee/commandeer"
	"github.com/jaffee/commandeer/examples/myapp"
)

func main() {
	err := commandeer.Run(myapp.NewMain())
	if err != nil {
		fmt.Println(err)
	}
}

Now...

$ ./myapp -h
Usage of ./myapp:
  -num int
    	How many does it take? (default 5)
  -vehicle string
    	What did they get? (default "jeep")

$ ./myapp
5 gophers stole my jeep!
$ ./myapp -num 3 -vehicle horse
3 gophers stole my horse!

Notice that Commandeer set up the default values for each flag based on the values in the struct passed to Run.

Commandeer is set up for minimal dependency pollution - it uses only stdlib dependencies and is a few hundred lines of code itself. You need only import it from a tiny main package (as in the example), and shouldn't need to reference it anywhere else.

If you aren't allergic to external dependencies, you can also try github.com/jaffee/commandeer/cobrafy which pulls in the excellent Cobra and pflag packages giving you GNU/POSIX style flags and some other nice features should you care to use them. See the godoc, or the myapp-cobrafy example.

Features

In addition to the help struct tag, you can use flag to override the computed flag name, e.g.

type Main struct {
	Num     int    `flag:"number"`
}

You can also use flag:"-" to explicitly ignore fields from being used as flags, e.g.

type Main struct {
	Num     int    `flag:"-"`
}

Nested structs are supported, by default the field names will be joined with "." to create the flag name, e.g.

type Main struct {
	Vehicle struct {
		Color string
		Weight int
	}
}

produces:

  -vehicle.color string
    	
  -vehicle.weight int

If you wish to avoid this prefix behavior (e.g. if you have an embedded struct field and you want to elevate its fields to the top level) you can use flag:"!embed", e.g.

type Main struct {
	Vehicle struct {
		Color string
		Weight int
	} `flag:"!embed"`
}

which will produce:

  -color string
    	
  -weight int

Contributing

Yes please!

For small stuff, feel free to submit a PR directly. For larger things, especially API changes, it's best to make an issue first so it can be discussed.

Similar projects

Documentation

Overview

Package commandeer sets up command line flags based on the fields and field tags of a struct. It helps ease common pains of CLI app development by allowing you to unobtrusively define flags in a library package while having a tiny main package which calls commandeer.Run* or commandeer.Flags.

Run is the usual interface to commandeer, but it requires you to pass in a struct which has a "Run() error" method. RunArgs works similarly, but allows you to pass in the args to be parsed and the flag set to be used. In cases where your struct doesn't have a Run() method, or you don't want to call it, the Flags() function takes in a FlagSet and sets the flags based on the passed in struct in the same way.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Flags

func Flags(flags Flagger, main interface{}) error

Flags sets up the given Flagger (usually an instance of flag.FlagSet or pflag.FlagSet). The second argument, "main", must be a pointer to a struct. A flag will be created for each exported field of the struct which isn't explicitly ignored.

Struct tags are used to control the behavior of Flags(), though none are necessary.

1. The "help" tag on a field is used to populate the usage string for that field's flag.

2. The "flag" tag on a field will be used as the name of that field's flag. Set it to "-" to ignore this field when creating flags. If it does not exist, the "json" tag will be used, and if it also does not exist, the field name will be downcased and converted from camel case to be dash separated.

3. The "short" tag on a field will be used as the shorthand flag for that field. It should be a single ascii character. This will only be used if the Flagger is also a PFlagger.

func LoadArgsEnv added in v0.2.0

func LoadArgsEnv(flags Flagger, main interface{}, args []string, envPrefix string, configElsewhere func(main interface{}) error) error

LoadArgsEnv uses Flags to define flags based on "main", then it tries setting each flag's value from the OS environment based on a prefix concatenated to the flag name. The flag name is normalized by removing any dashes or dots and replacing them with underscores.

One may also pass a "configElsewhere" function which can operate on main arbitrarily. The purpose of this is to load config values from (e.g.) a file without this package needing to import packages for parsing specific file formats.

Flags set via args take the highest precedence, followed by the environment, followed by configElsewhere (followed by defaults). Command line args and environment variables are set on main before it is passed to configElsewhere so that configElsewhere can be configured (such as with a path to a config file). Once configElsewhere runs, the environment and command line args are re-set since they take higher precedence.

func LoadEnv added in v0.2.0

func LoadEnv(main interface{}, envPrefix string, parseElsewhere func(main interface{}) error) error

LoadEnv calls LoadArgsEnv with args from the command line and the default flag set.

func Run

func Run(main interface{}) error

Run runs "main" which must be a pointer to a struct which implements the Runner interface. It first calls Flags to set up command line flags based on "main" (see the documentation for Flags).

func RunArgs

func RunArgs(flags Flagger, main interface{}, args []string) error

RunArgs is similar to Run, but the caller must specify their own flag set and args to be parsed by that flag set.

Types

type FlagNamer added in v0.3.0

type FlagNamer interface {
	Flags() []string
}

FlagNamer is an interface that Flaggers may use to list the available flags.

type Flagger

type Flagger interface {
	Parse([]string) error
	StringVar(p *string, name string, value string, usage string)
	IntVar(p *int, name string, value int, usage string)
	Int64Var(p *int64, name string, value int64, usage string)
	BoolVar(p *bool, name string, value bool, usage string)
	UintVar(p *uint, name string, value uint, usage string)
	Uint64Var(p *uint64, name string, value uint64, usage string)
	Float64Var(p *float64, name string, value float64, usage string)
	DurationVar(p *time.Duration, name string, value time.Duration, usage string)
	Set(name string, value string) error
}

Flagger is an interface satisfied by flag.FlagSet and other implementations of flags.

type PFlagger

type PFlagger interface {
	Parse([]string) error
	StringSliceVarP(p *[]string, name string, shorthand string, value []string, usage string)
	BoolSliceVarP(p *[]bool, name string, shorthand string, value []bool, usage string)
	UintSliceVarP(p *[]uint, name string, shorthand string, value []uint, usage string)
	IntSliceVarP(p *[]int, name string, shorthand string, value []int, usage string)
	IPSliceVarP(p *[]net.IP, name string, shorthand string, value []net.IP, usage string)
	Float32VarP(p *float32, name string, shorthand string, value float32, usage string)
	IPMaskVarP(p *net.IPMask, name string, shorthand string, value net.IPMask, usage string)
	IPNetVarP(p *net.IPNet, name string, shorthand string, value net.IPNet, usage string)
	IPVarP(p *net.IP, name string, shorthand string, value net.IP, usage string)
	Int16VarP(p *int16, name string, shorthand string, value int16, usage string)
	Int32VarP(p *int32, name string, shorthand string, value int32, usage string)
	Uint16VarP(p *uint16, name string, shorthand string, value uint16, usage string)
	Uint32VarP(p *uint32, name string, shorthand string, value uint32, usage string)
	Uint8VarP(p *uint8, name string, shorthand string, value uint8, usage string)
	Int8VarP(p *int8, name string, shorthand string, value int8, usage string)

	StringVarP(p *string, name string, shorthand string, value string, usage string)
	IntVarP(p *int, name string, shorthand string, value int, usage string)
	Int64VarP(p *int64, name string, shorthand string, value int64, usage string)
	BoolVarP(p *bool, name string, shorthand string, value bool, usage string)
	UintVarP(p *uint, name string, shorthand string, value uint, usage string)
	Uint64VarP(p *uint64, name string, shorthand string, value uint64, usage string)
	Float64VarP(p *float64, name string, shorthand string, value float64, usage string)
	DurationVarP(p *time.Duration, name string, shorthand string, value time.Duration, usage string)
}

PFlagger is an extension of the Flagger interface which is implemented by the pflag package (github.com/ogier/pflag, or github.com/spf13/pflag)

type Runner

type Runner interface {
	Run() error
}

Runner must be implemented by things passed to the Run and RunArgs methods.

type Value added in v0.4.0

type Value interface {
	String() string
	Set(string) error
	Type() string
}

Value is a copy of the pflag Value interface which is a superset of flag.Value

Source Files

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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