gli

package module
v2.3.0 Latest Latest
Warning

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

Go to latest
Published: Apr 25, 2024 License: BSD-2-Clause-Views Imports: 14 Imported by: 15

README

Go Report Card MIT License

features

  • struct base
  • tag (cli:"names, n" help:"help message" default:"parsable literal")
    • for sub commands (cli, help, usage)
    • for options (cli, help, default, env, required)
  • sub command as a member struct in a parent struct
    • sub sub ... command
  • extra sub command
  • user defined option types (example: gli.Range, gli.IntList, ...)
  • pointer type options
  • hook functions Init/Before/Run/After/Help as methods of commands

go get

go get github.com/shu-go/gli

example app:

go get github.com/shu-go/gli/example/todo

This introduces an executable binary todo.

Examples

Example1: Simple

type Global struct {
    Opt1 string
    Opt2 int
}

func main() {
    app := gli.New(&Global{})
    _, _, err := app.Run(os.Args)
    // :
}

func (g *Global) Run(args []string) {
}

// app --opt1 abc --opt2 123
// app --opt1=abc --opt2=123

Example2: Renaming

type Global struct {
    Opt1 string `cli:"s, str"`
    Opt2 int    `cli:"i, int, opt2"`
}

// :

// app --opt1 abc --opt2 123 <-- NG: opt1 is not defined (while opt2 is defined)
// app -s abc -i 123
// app --str abc --int 123

Example3: Sub command

type Global struct {
    Opt1 string `cli:"s, str"`
    Opt2 int    `cli:"i, int, opt2"`

    Sub1 mySub
}

type mySub struct {
    Opt3 string
    
    Sub2 mySubSub `cli:s, sub2`
}

func (sub *mySub) Run(g *Global, args []string) {
}

func (subsub *mySubSub) Run(g *Global, args []string, sub *mySub) {
}

// app --str abc --int 123 sub1 --opt3 def

Example4: Hook functions

Commnds (root and sub commands) may have some hook functions.

Define receivers for the target commands.

func (subsub *mySubSub) Run(g *Global, args []string, sub *mySub) {
}
  • Run
    • is called for the target command
  • Before
    • are called for root -> sub -> subsub(target, in this case)
    • With any error, Run is not called.
  • After
    • are called for subsub(target, in this case) -> sub -> root
  • Init
    • are called for root -> sub -> subsub(target, in this case)
    • These functions are for initialization of command struct.
  • Help
    • prints help message.
    • subsub(target, in this case) -> root
Run
  1. Init for all commands
  2. Before for all commands
    • and defer calling After
  3. Run
Help
  1. Init for all commands
  2. first defined Help, subsub -> root
Signature

Parameters are in arbitrary order, omittable.

  • []string
  • struct{...} or *struct{...} of command
// OK
func (subsub *mySubSub) Run(args []string, g *Global, sub *mySub) error {
}
// OK
func (subsub *mySubSub) Run(g *Global, args []string, sub *mySub) error {
}
// OK
func (subsub *mySubSub) Run(args []string, sub *mySub) error {
}
// OK
func (subsub *mySubSub) Run(sub *mySub) error {
}

Return value is nothing or an error.

func (subsub *mySubSub) Run() {
}
func (subsub *mySubSub) Run() error {
}

Example5: No Hook

Using gli to get values. No Run() implemented.

type Global struct {
    Opt1 string
    Opt2 int
    Sub1 *Sub1Cmd
}

type Sub1Cmd struct {
    Opt3 string
}

func main() {
    g := Global{}
    app := gli.New(&g)
    tgt, tgtargs, err := app.Run(os.Args, false) // no hook

    // traverse g
    println(g.Opt1) // abc
    println(g.Opt2) // 123
    if g.Sub1 != nil {
        println(g.Sub1.Opt3) // def
    }

    if sub1, ok := tgt.(*Sub1Cmd); ok {
        println(sub1.Opt3) // def
    }
    println(tgtargs) // g h i
}

func (g *Global) Run(args []string) {
    // not called
}

// app --opt1 abc --opt2 123 sub1 --opt3 def  g h i

Example6: Extra Command

type Global struct {}

func main() {
    ex := extra{
        Name string `cli:"n"`
    }{}

    app := gli.New(&Global{})
    app.AddExtraCommand(&ex, "extra", "help message")
}

// app extra -n abc

Example7: User defined option types

type MyOption struct {
    Data int
}

func (o *MyOption) Parse(s string) error {
    o.Data = len(s)
}

//

type Global struct {
    My MyOption
}

Example8: more tags

type Global struct {
    Opt1 string `cli:"opt1=PLACE_HOLDER" default:"default value" env:"ENV_OPT1" help:"help message"`
    Opt2 int    `cli:"opt2" required:"true"`
    Sub MySub   `cli:"sub" help:"help message" usage:"multi line usages\nseparated by \\n"`
}

Options:

  • cli
    • renaming
    • cli:"name1, name2, ..., nameZ=PLACE_HOLDER"
  • default
    • in string literal
    • bool, float, int and uint are converted by strconv.ParseXXX
    • other types are required implement func Parse (see Example7)
    • use Init hook function for dynamic default values
  • env
    • environment variable name
  • required
    • bool (true/false)
      • true if the option is given in the command line
    • checked just before Before or Run hook function is executed
    • this check is not affected by Init, Before, After hook function nor default tag
  • help

Sub commands:

  • cli
  • help
  • usage
    • multi line usage description separated by \n.

Option value overwriting:

  1. default tag
  2. env tag
  3. Init hook function

Example9: alternative help and usage of commands

type Global struct {
    Sub1 SubCommand1 `cli:"s1"  help:"a command"  usage:"s1 [anything]"`
    Sub2 SubCommand2 `cli:"s2"` // no help and usage
}

type SubCommand2 struct {
    help struct{} `help:"another command" usage:"s2 [something]"`

    // Underscore is also OK.
    //_ struct{} `help:"another command" usage:"s2 [something]"` 
}

Both Sub1 and Sub2 are handled as have same tags.


Copyright 2018 Shuhei Kubota

Documentation

Overview

Package gli is a CLI parsing and mapping library.

type globalCmd struct {
    Name string
    Age  int
}
func (g *globalCmd) Run() error {
    // :
}
app := gli.NewWith(&globalCmd{})
err := app.Run(os.Args)

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotDefined means "an option or a subcommand is not defined in the passed struct".
	ErrNotDefined = errors.New("not defined")
	// ErrNotRunnable means "Run method is not defined for the passed struct".
	ErrNotRunnable = fmt.Errorf("command not runnable")
	// ErrOptCanNotBeSet is a reflect related error.
	ErrOptCanNotBeSet = fmt.Errorf("option can not be set")
)

Functions

func RegisterTypeDecoder

func RegisterTypeDecoder(typ interface{}, dec TypeDecoder)

See TypeDecoder.

func Usage

func Usage(usage string) extraCmdInit

Usage is an optional argument to AddExtracommand.

Types

type App

type App struct {

	// Name is the app name. default: the name of the executable file
	Name string
	// Desc is a description of the app
	// {{Name}} - {{Desc}}
	Desc string
	// Usage is a long(multiline) usage text
	Usage string
	// Version numbers
	Version string

	// Copyright the app author has
	Copyright string

	// CliTag is a tag key. default: `cli`
	CliTag string
	// HelpTag is a tag key. default: `help`
	HelpTag string
	// UsageTag is a tag key. default: `usage`
	UsageTag string
	// DefaultTag is a tag key. default: `default`
	DefaultTag string
	// DefDescTag is a tag key. default: `defdesc`
	DefDescTag string
	// EnvTag is a tag key. default: `env`
	EnvTag string
	// RequiredTag is a tag key. default: `required`
	RequiredTag string
	// DecTypeTag is a tag key that is used to lookup decoder. default: `type`
	DecTypeTag string

	// MyCommandABC => false(default): "mycommandabc" , true: "my-command-abc"
	HyphenedCommandName bool
	// MyOptionABC => false(default): "myoptionabc" , true: "my-option-abc"
	HyphenedOptionName bool
	// OptionsGrouped(default: true) allows -abc may be treated as -a -b -c.
	OptionsGrouped bool
	DoubleHyphen   bool

	// SuppressErrorOutput is an option to suppresses on cli parsing error.
	SuppressErrorOutput bool
	Stdout, Stderr      *os.File

	// true(default): bool options have --no-xxx options.
	// AutoNoBoolOptions also appends --no-xxx descriptions in help doc if .
	//
	// Options:
	//   opt1, o1  A bool option
	//   opt2, o2  A bool option (default: true)
	//     --no-opt2
	//   opt1, o1  A bool option
	AutoNoBoolOptions bool
	// contains filtered or unexported fields
}

App contains parsing and parsed data.

func New

func New() App

New makes main gli instance to parse and invoke hooks.

App bridges between application logic code and CLI definition (your struct).

Next, call Bind() to set the CLI up.

func NewWith

func NewWith(ptrSt interface{}) App

NewWith is New().Bind(ptrSt)

func (*App) AddExtraCommand

func (g *App) AddExtraCommand(ptrSt interface{}, names, help string, inits ...extraCmdInit)

AddExtraCommand adds a sub command.

func (*App) Bind

func (g *App) Bind(ptrSt interface{}) error

Bind updates option/command names with ptrSt. Extra commands are cleared.

func (App) Help

func (g App) Help(w io.Writer)

Help displays help messages.

func (*App) Parse

func (g *App) Parse(args []string) (tgt interface{}, tgtargs []string, err error)

Parse parses args and returns results. tgt (interface{}) : a resultant struct tgtargs ([]string) : args of last subcommand err : parsing error

func (*App) Run

func (g *App) Run(args []string) error

Run parses args and calls Run method of a subcommand.

type Range

type Range struct {
	Min, Max string
}

type Separator added in v2.3.0

type Separator string
type MyCommand struct {
  Separator1 gli.Separator
  Separator2 string `type:"Separator"`
}

type SeparatorRune added in v2.3.0

type SeparatorRune rune
type MyCommand struct {
  Separator1 gli.SeparatorRune
  Separator2 string `type:"SeparatorRune"`
}

type TypeDecoder

type TypeDecoder func(s string, v reflect.Value, tag reflect.StructTag, firstTime bool) error

TypeDecoder is used to convert command-line arguments to optional values. It decodes a string to another type of value.

To be decoded

If you declare your cli interface like:

type myCLIRootCommand struct {
    Opt1 []string `cli:"opt1"`
}

And end-user gives a command-line like:

my.exe  --opt1 a,b,c

The library gli finds that the opt1 is of type []string, and tries to decode the string "a,b,c" into []string.

gli finds a decoder by calling LookupTypeDecoder. And then gli calls the decoder function to decode "a,b,c" into []string{"a","b","c"}. Finally, gli assigns []string{"a","b","c"} to opt1.

Implemented types (enabled by default)

  • (built-in types of golang)
  • time.Time (local time; --opt yyyy/mm/dd or --opt yyyy-mm-dd)
  • time.Duration
  • []string (--opt a,b,c)
  • []int (--opt 1,2,3)
  • map[string]string (--opt key:value,key:value)
  • gli.Range{Min,Max string} (--opt min:max)

User defined types

  1. Define a decoder function as TypeDecoder
  2. Call gli.RegisterTypeDecoder(reflect.TypeOf(anyValueOfTheType), decoderFunc)

TypeDecoder

s is a string to decode.

tag is a StructTag of the option.

If firstTime is true, the function should reset v and then decode s into v. Otherwise, it simply decodes s into v. This parameter is useful when appending the contents of a value.

func LookupTypeDecoder

func LookupTypeDecoder(typ interface{}) TypeDecoder

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

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