cli

package module
v0.2.10 Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2024 License: MIT Imports: 27 Imported by: 0

README

Command line interface

License Travis branch Coverage Status Go Report Card GoDoc

Screenshot

screenshot2

Key features

  • Lightweight and easy to use.
  • Defines flag by tag, e.g. flag name(short or/and long), description, default value, password, prompt and so on.
  • Type safety.
  • Output looks very nice.
  • Supports custom Validator.
  • Supports slice and map as a flag.
  • Supports any type as a flag field which implements cli.Decoder interface.
  • Supports any type as a flag field which uses FlagParser.
  • Suggestions for command.(e.g. hl => help, "veron" => "version").
  • Supports default value for flag, even expression about env variable(e.g. dft:"$HOME/dev").
  • Supports editor like git commit command.(See example 21 and 22)

API documentation

See godoc

Examples

Example 1: Hello

back to examples

// main.go
// This is a HelloWorld-like example

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	Name string `cli:"name" usage:"tell me your name"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("Hello, %s!\n", argv.Name)
		return nil
	}))
}
$ go build -o hello
$ ./hello --name Clipher
Hello, Clipher!
Example 2: Flag

back to examples

// main.go
// This example show basic usage of flag

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	cli.Helper
	Port int  `cli:"p,port" usage:"short and long format flags both are supported"`
	X    bool `cli:"x" usage:"boolean type"`
	Y    bool `cli:"y" usage:"boolean type, too"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("port=%d, x=%v, y=%v\n", argv.Port, argv.X, argv.Y)
		return nil
	}))
}
$ go build -o app
$ ./app -h
Options:

  -h, --help     display help information
  -p, --port     short and long format flags both are supported
  -x             boolean type
  -y             boolean type, too
$ ./app -p=8080 -x
port=8080, x=true, y=false
$ ./app -p 8080 -x=true
port=8080, x=true, y=false
$ ./app -p8080 -y true
port=8080, x=false, y=true
$ ./app --port=8080 -xy
port=8080, x=true, y=true
$ ./app --port 8080 -yx
port=8080, x=true, y=true
Example 3: Required flag

back to examples

// main.go
// This example show how to use required flag

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	cli.Helper
	Id uint8 `cli:"*id" usage:"this is a required flag, note the *"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("%d\n", argv.Id)
		return nil
	}))
}
$ go build -o app
$ ./app
ERR! required argument --id missing
$ ./app --id=2
2
Example 4: Default flag

back to examples

// main.go
// This example show how to use default flag

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	cli.Helper
	Basic  int    `cli:"basic" usage:"basic usage of default" dft:"2"`
	Env    string `cli:"env" usage:"env variable as default" dft:"$HOME"`
	Expr   int    `cli:"expr" usage:"expression as default" dft:"$BASE_PORT+1000"`
	DevDir string `cli:"devdir" usage:"directory of developer" dft:"$HOME/dev"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("%d, %s, %d, %s\n", argv.Basic, argv.Env, argv.Expr, argv.DevDir)
		return nil
	}))
}
$ go build -o app
$ ./app -h
Options:

  -h, --help                       display help information
      --basic[=2]                  basic usage of default
      --env[=$HOME]                env variable as default
      --expr[=$BASE_PORT+1000]     expression as default
      --devdir[=$HOME/dev]         directory of developer
$ ./app
2, /Users/wang, 1000, /Users/wang/dev
$ BASE_PORT=8000 ./app --basic=3
3, /Users/wang, 9000, /Users/wang/dev
Example 5: Slice

back to examples

// main.go
// This example show how to use slice as a flag

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	// []bool, []int, []float32, ... supported too.
	Friends []string `cli:"F" usage:"my friends"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		ctx.JSONln(ctx.Argv())
		return nil
	}))
}
$ go build -o app
$ ./app
{"Friends":null}
$ ./app -FAlice -FBob -F Charlie
{"Friends":["Alice","Bob","Charlie"]}
Example 6: Map

back to examples

// main.go
// This example show how to use map as a flag

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	Macros map[string]int `cli:"D" usage:"define macros"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		ctx.JSONln(ctx.Argv())
		return nil
	}))
}
$ go build -o app
$ ./app
{"Macros":null}
$ ./app -Dx=not-a-number
ERR! `not-a-number` couldn't converted to an int value
$ ./app -Dx=1 -D y=2
{"Macros":{"x":1,"y":2}}
Example 7: Force flag

back to examples

// main.go
// This example show usage of force flag
// Force flag has prefix !, and must be a boolean.
// Will prevent validating flags if some force flag assigned true

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	Version  bool `cli:"!v" usage:"force flag, note the !"`
	Required int  `cli:"*r" usage:"required flag"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		if argv.Version {
			ctx.String("v0.0.1\n")
		}
		return nil
	}))
}
$ go build -o app
$ ./app
ERR! required argument -r missing

# -v is a force flag, and assigned true, so `ERR` disappear.
$ ./app -v
v0.0.1
Example 8: Child command

back to examples

// main.go
// This example demonstrates usage of child command

package main

import (
	"fmt"
	"os"

	"github.com/akeylesslabs/cli"
)

func main() {
	if err := cli.Root(root,
		cli.Tree(help),
		cli.Tree(child),
	).Run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

var help = cli.HelpCommand("display help information")

// root command
type rootT struct {
	cli.Helper
	Name string `cli:"name" usage:"your name"`
}

var root = &cli.Command{
	Desc: "this is root command",
	// Argv is a factory function of argument object
	// ctx.Argv() is if Command.Argv == nil or Command.Argv() is nil
	Argv: func() interface{} { return new(rootT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*rootT)
		ctx.String("Hello, root command, I am %s\n", argv.Name)
		return nil
	},
}

// child command
type childT struct {
	cli.Helper
	Name string `cli:"name" usage:"your name"`
}

var child = &cli.Command{
	Name: "child",
	Desc: "this is a child command",
	Argv: func() interface{} { return new(childT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*childT)
		ctx.String("Hello, child command, I am %s\n", argv.Name)
		return nil
	},
}
$ go build -o app

# help for root
# equivalent to "./app -h"
$ ./app help
this is root command

Options:

  -h, --help     display help information
      --name     your name

Commands:
  help    display help information
  child   this is a child command

# help for specific command
# equivalent to "./app child -h"
$ ./app help child
this is a child command

Options:

  -h, --help     display help information
      --name     your name

# execute root command
$ ./app --name 123
Hello, root command, I am 123

# execute child command
$ ./app child --name=123
Hello, child command, I am 123

# something wrong, but got a suggestion.
$ ./app chd
ERR! command chd not found
Did you mean child?
Example 9: Auto help

back to examples

// main.go
// This example demonstrates cli.AutoHelper

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	Help bool `cli:"h,help" usage:"show help"`
}

// AutoHelp implements cli.AutoHelper interface
// NOTE: cli.Helper is a predefined type which implements cli.AutoHelper
func (argv *argT) AutoHelp() bool {
	return argv.Help
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		return nil
	}))
}
$ go build -o app
$ ./app -h
Options:

  -h, --help     show help

Try comment AutoHelp method and rerun it.

Example 10: Usage of Validator

back to examples

// main.go
// This example demonstrates how to utilize Validator

package main

import (
	"fmt"
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	cli.Helper
	Age    int    `cli:"age" usage:"your age"`
	Gender string `cli:"g,gender" usage:"your gender" dft:"male"`
}

// Validate implements cli.Validator interface
func (argv *argT) Validate(ctx *cli.Context) error {
	if argv.Age < 0 || argv.Age > 300 {
		return fmt.Errorf("age %d out of range", argv.Age)
	}
	if argv.Gender != "male" && argv.Gender != "female" {
		return fmt.Errorf("invalid gender %s", ctx.Color().Yellow(argv.Gender))
	}
	return nil
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		ctx.JSONln(ctx.Argv())
		return nil
	}))
}
$ go build -o app
$ ./app --age=-1
ERR! age -1 out of range
$ ./app --age=1000
ERR! age 1000 out of range
$ ./app -g balabala
ERR! invalid gender balabala
$ ./app --age 88 --gender female
{"Help":false,"Age":88,"Gender":"female"}
Example 11: Prompt and Password

back to examples

// main.go
// This example introduce prompt and pw tag

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	cli.Helper
	Username string `cli:"u,username" usage:"github account" prompt:"type github account"`
	Password string `pw:"p,password" usage:"password of github account" prompt:"type the password"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("username=%s, password=%s\n", argv.Username, argv.Password)
		return nil
	}))
}
$ go build -o app
$ ./app
type github account: hahaha # visible
type the password: # invisible because of `pw` tag
username=hahaha, password=123456
Example 12: Decoder

back to examples

// main.go
// This example show how to use decoder

package main

import (
	"os"
	"strings"

	"github.com/akeylesslabs/cli"
)

type exampleDecoder struct {
	list []string
}

// Decode implements cli.Decoder interface
func (d *exampleDecoder) Decode(s string) error {
	d.list = strings.Split(s, ",")
	return nil
}

type argT struct {
	Example exampleDecoder `cli:"d" usage:"example decoder"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.JSONln(argv.Example.list)
		return nil
	}))
}
$ go build -o app
$ ./app -d a,b,c
["a","b","c"]
Example 13: Pid file

back to examples

// main.go
// This example show how to use builtin Decoder: PidFile

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
	clix "github.com/akeylesslabs/cli/ext"
)

type argT struct {
	cli.Helper
	PidFile clix.PidFile `cli:"pid" usage:"pid file" dft:"013-pidfile.pid"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)

		if err := argv.PidFile.New(); err != nil {
			return err
		}
		defer argv.PidFile.Remove()

		return nil
	}))
}
Example 14: Time and Duration

back to examples

// main.go
// This example show how to use builtin Decoder: Time and Duration

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
	clix "github.com/akeylesslabs/cli/ext"
)

type argT struct {
	Time     clix.Time     `cli:"t" usage:"time"`
	Duration clix.Duration `cli:"d" usage:"duration"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("time=%v, duration=%v\n", argv.Time, argv.Duration)
		return nil
	}))
}
$ go build -o app
$ ./app -t '2016-1-2 3:5' -d=10ms
time=2016-01-02 03:05:00 +0800 CST, duration=10ms
Example 15: File

back to examples

// main.go
// This example show how to use builtin Decoder: File

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
	clix "github.com/akeylesslabs/cli/ext"
)

type argT struct {
	Content clix.File `cli:"f,file" usage:"read content from file or stdin"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String(argv.Content.String())
		return nil
	}))
}
$ go build -o app
# read from stdin
$ echo hello | ./app -f
hello
# read from file
$ echo hello > test.txt && ./app -f test.txt
hello
$ rm test.txt
Example 16: Parser

back to examples

// main.go
// This example introduce Parser
// `Parser` is another way to use custom type of data.
// Unlike `Decoder`, `Parser` used to parse string according to specific rule,
// like json,yaml and so on.
//
// Builtin parsers:
// * json
// * jsonfile

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type config struct {
	A string
	B int
	C bool
}

type argT struct {
	JSON config `cli:"c,config" usage:"parse json string" parser:"json"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.JSONIndentln(argv.JSON, "", "    ")
		return nil
	}))
}
$ go build -o app
$ ./app
{
    "A": "",
    "B": 0,
    "C": false
}
$ ./app -c '{"A": "hello", "b": 22, "C": true}'
{
    "A": "hello",
    "B": 22,
    "C": true
}
Example 17: JSON file

back to examples

// main.go
// This example show how to use builtin parser: jsonfile
// It's similar to json, but read string from file.

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type config struct {
	A string
	B int
	C bool
}

type argT struct {
	JSON config `cli:"c,config" usage:"parse json from file" parser:"jsonfile"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.JSONIndentln(argv.JSON, "", "    ")
		return nil
	}))
}
$ go build -o app
$ echo '{"A": "hello", "b": 22, "C": true}' > test.json
$ ./app -c test.json
{
    "A": "hello",
    "B": 22,
    "C": true
}
$ rm test.json
Example 18: Custom parser

back to examples

// main.go
// This example demonstrates how to use custom parser

package main

import (
	"os"
	"reflect"

	"github.com/akeylesslabs/cli"
)

type myParser struct {
	ptr interface{}
}

func newMyParser(ptr interface{}) cli.FlagParser {
	return &myParser{ptr}
}

// Parse implements FlagParser.Parse interface
func (parser *myParser) Parse(s string) error {
	typ := reflect.TypeOf(parser.ptr)
	val := reflect.ValueOf(parser.ptr)
	if typ.Kind() == reflect.Ptr {
		kind := reflect.Indirect(val).Type().Kind()
		if kind == reflect.Struct {
			typElem, valElem := typ.Elem(), val.Elem()
			numField := valElem.NumField()
			for i := 0; i < numField; i++ {
				_, valField := typElem.Field(i), valElem.Field(i)
				if valField.Kind() == reflect.Int &&
					valField.CanSet() {
					valField.SetInt(2)
				}
				if valField.Kind() == reflect.String &&
					valField.CanSet() {
					valField.SetString("B")
				}
			}
		}
	}
	return nil
}

type config struct {
	A int
	B string
}

type argT struct {
	Cfg config `cli:"cfg" parser:"myparser"`
}

func main() {
	// register parser factory function
	cli.RegisterFlagParser("myparser", newMyParser)

	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("%v\n", argv.Cfg)
		return nil
	}))
}
$ go build -o app
$ ./app
{0 }
$ ./app --cfg xxx
{2 B}
Example 19: Hooks

back to examples

// main.go
// This example demonstrates how to use hooks

package main

import (
	"fmt"
	"os"

	"github.com/akeylesslabs/cli"
)

func main() {
	if err := cli.Root(root,
		cli.Tree(child1),
		cli.Tree(child2),
	).Run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

type argT struct {
	Error bool `cli:"e" usage:"return error"`
}

var root = &cli.Command{
	Name: "app",
	Argv: func() interface{} { return new(argT) },
	OnRootBefore: func(ctx *cli.Context) error {
		ctx.String("OnRootBefore invoked\n")
		return nil
	},
	OnRootAfter: func(ctx *cli.Context) error {
		ctx.String("OnRootAfter invoked\n")
		return nil
	},
	Fn: func(ctx *cli.Context) error {
		ctx.String("exec root command\n")
		argv := ctx.Argv().(*argT)
		if argv.Error {
			return fmt.Errorf("root command returns error")
		}
		return nil
	},
}

var child1 = &cli.Command{
	Name: "child1",
	Argv: func() interface{} { return new(argT) },
	OnBefore: func(ctx *cli.Context) error {
		ctx.String("child1's OnBefore invoked\n")
		return nil
	},
	OnAfter: func(ctx *cli.Context) error {
		ctx.String("child1's OnAfter invoked\n")
		return nil
	},
	Fn: func(ctx *cli.Context) error {
		ctx.String("exec child1 command\n")
		argv := ctx.Argv().(*argT)
		if argv.Error {
			return fmt.Errorf("child1 command returns error")
		}
		return nil
	},
}

var child2 = &cli.Command{
	Name:   "child2",
	NoHook: true,
	Fn: func(ctx *cli.Context) error {
		ctx.String("exec child2 command\n")
		return nil
	},
}
$ go build -o app
# OnRootBefore => Fn => OnRootAfter
$ ./app
OnRootBefore invoked
exec root command
OnRootAfter invoked
# OnBefore => OnRootBefore => Fn => OnRootAfter => OnAfter
$ ./app child1
child1 OnBefore invoked
OnRootBefore invoked
exec child1 command
OnRootAfter invoked
child1 OnAfter invoked
# No hooks
$ ./app child2
exec child2 command
# OnRootBefore => Fn --> Error
$ ./app -e
OnRootBefore invoked
exec root command
root command returns error
# OnBefore => OnRootBefore => Fn --> Error
$ ./app child1 -e
child1 OnBefore invoked
OnRootBefore invoked
exec child1 command
child1 command returns error
Example 20: Daemon

back to examples

// main.go
// This example demonstrates how to use `Daemon`

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	cli.Helper
	Wait  uint `cli:"wait" usage:"seconds for waiting" dft:"10"`
	Error bool `cli:"e" usage:"create an error"`
}

const successResponsePrefix = "start ok"

func main() {
	if err := cli.Root(root,
		cli.Tree(daemon),
	).Run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

var root = &cli.Command{
	Argv: func() interface{} { return new(argT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		if argv.Error {
			err := fmt.Errorf("occurs error")
			cli.DaemonResponse(err.Error())
			return err
		}
		cli.DaemonResponse(successResponsePrefix)
		<-time.After(time.Duration(argv.Wait) * time.Second)
		return nil
	},
}

var daemon = &cli.Command{
	Name: "daemon",
	Argv: func() interface{} { return new(argT) },
	Fn: func(ctx *cli.Context) error {
		return cli.Daemon(ctx, successResponsePrefix)
	},
}
$ go build -o daemon-app
$ ./daemone-app daemon
start ok
# Within 10 seconds, you will see process "./daemon-app"
$ ps | grep daemon-app
11913 ttys002    0:00.01 ./daemon-app
11915 ttys002    0:00.00 grep daemon-app
# After 10 seconds
$ ps | grep daemon-app
11936 ttys002    0:00.00 grep daemon-app
# try again with an error
$ ./daemon-app daemon -e
occurs error
$ ps | grep daemon-app
11936 ttys002    0:00.00 grep daemon-app
Example 21: Editor

back to examples

// main.go
// This example demonstrates how to use `editor`. This similar to git commit

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	cli.Helper
	Msg string `edit:"m" usage:"message"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("msg: %s", argv.Msg)
		return nil
	}))
}
$ go build -o app
$ ./app -m "hello, editor"
msg: hello, editor
$ ./app # Then, launch a editor(default is vim) and type `hello, editor`, quit the editor
msg: hello, editor
Example 22: Custom Editor

back to examples

// main.go
// This example demonstrates specific editor.

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

type argT struct {
	cli.Helper
	Msg string `edit:"m" usage:"message"`
}

func main() {
	cli.GetEditor = func() (string, error) {
		if editor := os.Getenv("EDITOR"); editor != "" {
			return editor, nil
		}
		return cli.DefaultEditor, nil
	}
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("msg: %s", argv.Msg)
		return nil
	}))
}
$ go build -o app
$ ./app -m "hello, editor"
msg: hello, editor
$ EDITOR=nano ./app # Then, launch nano and type `hello, editor`, quit the editor
msg: hello, editor

Documentation

Overview

Example (Hello)

This is a HelloWorld example

package main

import (
	"github.com/akeylesslabs/cli"
)

type helloT struct {
	cli.Helper
	Name string `cli:"name" usage:"tell me your name" dft:"world"`
	Age  uint8  `cli:"a,age" usage:"tell me your age" dft:"100"`
}

func main() {
	args := []string{"app", "--name=Cliper"}
	cli.RunWithArgs(new(helloT), args, func(ctx *cli.Context) error {
		argv := ctx.Argv().(*helloT)
		ctx.String("Hello, %s! Your age is %d?\n", argv.Name, argv.Age)
		return nil
	})
}
Output:

Hello, Cliper! Your age is 100?

Index

Examples

Constants

View Source
const DefaultEditor = "vim"

Variables

View Source
var ExitError = exitError{}

ExitError is a special error, should be ignored but return

View Source
var GetEditor func() (string, error)

GetEditor sets callback to get editor program

Functions

func Daemon

func Daemon(ctx *Context, successPrefix string) error

Daemon startup app as a daemon process, success if result from stderr has prefix successPrefix

func DaemonResponse

func DaemonResponse(resp string)

DaemonResponse output response to stderr

func HelpCommandFn

func HelpCommandFn(ctx *Context) error

HelpCommandFn implements buildin help command function

func IsValidCommandName

func IsValidCommandName(commandName string) bool

IsValidCommandName validates name of command

func LaunchEditor

func LaunchEditor(editor string) (content []byte, err error)

LaunchEditor launchs the specified editor with a random filename

func Parse

func Parse(args []string, argv interface{}) error

Parse parses args to object argv

Example (DefaultValue)

This example demonstrates how to use default value

package main

import (
	"os"

	"github.com/akeylesslabs/cli"
)

func main() {
	type argT1 struct {
		Port int `cli:"p,port" usage:"listening port" dft:"8080"`
	}
	type argT2 struct {
		Port int `cli:"p,port" usage:"listening port" dft:"$CLI_TEST_HTTP_PORT"`
	}
	type argT3 struct {
		Port int `cli:"p,port" usage:"listening port" dft:"$CLI_TEST_HTTP_PORT+800"`
	}
	type argT4 struct {
		DevDir string `cli:"dir" usage:"develope directory" dft:"$CLI_TEST_DEV_PARENT_DIR/dev"`
	}

	os.Setenv("CLI_TEST_DEV_PARENT_DIR", "/home")
	os.Setenv("CLI_TEST_HTTP_PORT", "8000")

	for _, tt := range []struct {
		argv interface{}
		args []string
	}{
		{new(argT1), []string{"app"}},
		{new(argT2), []string{"app"}},
		{new(argT3), []string{"app"}},
		{new(argT4), []string{"app"}},
		{new(argT4), []string{"app", "--dir=/dev"}},
	} {
		cli.RunWithArgs(tt.argv, tt.args, func(ctx *cli.Context) error {
			ctx.String("argv=%v\n", ctx.Argv())
			return nil
		})
	}
}
Output:

argv=&{8080}
argv=&{8000}
argv=&{8800}
argv=&{/home/dev}
argv=&{/dev}
Example (ShortAndLongFlagName)

This example demonstrates how to use short and long format flag

package main

import (
	"github.com/akeylesslabs/cli"
)

func main() {
	// argument object
	type argT struct {
		Port int `cli:"p,port" usage:"listening port"`
	}

	for _, args := range [][]string{
		[]string{"app", "-p", "8080"},
		[]string{"app", "-p8081"},
		[]string{"app", "-p=8082"},
		[]string{"app", "--port", "8083"},
		[]string{"app", "--port=8084"},
	} {
		cli.RunWithArgs(&argT{}, args, func(ctx *cli.Context) error {
			argv := ctx.Argv().(*argT)
			ctx.String("port=%d\n", argv.Port)
			return nil
		})
	}
}
Output:

port=8080
port=8081
port=8082
port=8083
port=8084
Example (SliceAndMap)

This example demonstrates to use Slice and Map

package main

import (
	"github.com/akeylesslabs/cli"
)

func main() {
	type argT1 struct {
		Slice []uint32 `cli:"U,u32-slice" usage:"uint32 slice"`
	}
	type argT2 struct {
		Slice []string `cli:"S,str-slice" usage:"string slice"`
	}
	type argT3 struct {
		Slice []bool `cli:"B,bool-slice" usage:"boolean slice"`
	}
	type argT4 struct {
		MapA map[string]int  `cli:"A" usage:"string => int"`
		MapB map[int]int     `cli:"B" usage:"int => int"`
		MapC map[int]string  `cli:"C" usage:"int => string"`
		MapD map[string]bool `cli:"D" usage:"string => bool"`
	}

	for _, tt := range []struct {
		argv interface{}
		args []string
	}{
		{new(argT1), []string{"app", "-U1", "-U2"}},
		{new(argT1), []string{"app", "-U", "1", "-U", "2"}},
		{new(argT1), []string{"app", "--u32-slice", "1", "--u32-slice", "2"}},
		{new(argT2), []string{"app", "-Shello", "-Sworld"}},
		{new(argT2), []string{"app", "-S", "hello", "-S", "world"}},
		{new(argT2), []string{"app", "--str-slice", "hello", "--str-slice", "world"}},
		{new(argT3), []string{"app", "-Btrue", "-Bfalse"}},
		{new(argT3), []string{"app", "-B", "true", "-B", "false"}},
		{new(argT3), []string{"app", "--bool-slice", "true", "--bool-slice", "false"}},

		{new(argT4), []string{"app",
			"-Ax=1",
			"-B", "1=2",
			"-C1=a",
			"-Dx",
		}},
	} {
		cli.RunWithArgs(tt.argv, tt.args, func(ctx *cli.Context) error {
			ctx.String("argv=%v\n", ctx.Argv())
			return nil
		})
	}
}
Output:

argv=&{[1 2]}
argv=&{[1 2]}
argv=&{[1 2]}
argv=&{[hello world]}
argv=&{[hello world]}
argv=&{[hello world]}
argv=&{[true false]}
argv=&{[true false]}
argv=&{[true false]}
argv=&{map[x:1] map[1:2] map[1:a] map[x:true]}

func ReadJSON added in v0.0.2

func ReadJSON(r io.Reader, argv interface{}) error

ReadJSON reads data as a json structure into argv

func ReadJSONConfigFromFile added in v0.0.3

func ReadJSONConfigFromFile(filename string, argv interface{}) error

ReadJSONConfigFromFile is similar to ReadJSONFromFile, but allows reading file from where the executable file resides as well

func ReadJSONFromFile added in v0.0.2

func ReadJSONFromFile(filename string, argv interface{}) error

ReadJSONFromFile is similar to ReadJSON, but read from file

func RegisterFlagParser

func RegisterFlagParser(name string, creator FlagParserCreator)

RegisterFlagParser registers FlagParserCreator by name

Example

This example demonstrates how to use custom parser

package main

import (
	"reflect"

	"github.com/akeylesslabs/cli"
)

type myParser struct {
	ptr interface{}
}

func newMyParser(ptr interface{}) cli.FlagParser {
	return &myParser{ptr}
}

// Parse implements FlagParser.Parse interface
func (parser *myParser) Parse(s string) error {
	typ := reflect.TypeOf(parser.ptr)
	val := reflect.ValueOf(parser.ptr)
	if typ.Kind() == reflect.Ptr {
		kind := reflect.Indirect(val).Type().Kind()
		if kind == reflect.Struct {
			typElem, valElem := typ.Elem(), val.Elem()
			numField := valElem.NumField()
			for i := 0; i < numField; i++ {
				_, valField := typElem.Field(i), valElem.Field(i)
				if valField.Kind() == reflect.Int && valField.CanSet() {
					valField.SetInt(2)
				}
				if valField.Kind() == reflect.String && valField.CanSet() {
					valField.SetString("B")
				}
			}
		}
	}
	return nil
}

type config3 struct {
	A int
	B string
}

func main() {
	// register parser factory function
	cli.RegisterFlagParser("myparser", newMyParser)

	type argT struct {
		Cfg3 config3 `cli:"cfg3" parser:"myparser"`
	}

	args := []string{"app",
		`--cfg3`, `hello`,
	}

	cli.RunWithArgs(new(argT), args, func(ctx *cli.Context) error {
		ctx.JSON(ctx.Argv())
		return nil
	})
}
Output:

{"Cfg3":{"A":2,"B":"B"}}

func Run

func Run(argv interface{}, fn CommandFunc, descs ...string) int

Run runs a single command app

Example
package main

import (
	"github.com/akeylesslabs/cli"
)

func main() {
	type argT struct {
		Flag string `cli:"f"`
	}
	cli.RunWithArgs(new(argT), []string{"app", "-f=xxx"}, func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("flag: %s\n", argv.Flag)
		return nil
	})
}
Output:

flag: xxx

func RunWithArgs

func RunWithArgs(argv interface{}, args []string, fn CommandFunc, descs ...string) int

RunWithArgs is similar to Run, but with args instead of os.Args

func SetUsageStyle

func SetUsageStyle(style UsageStyle)

SetUsageStyle sets default style

Types

type Addr deprecated

type Addr struct {
	Host string `cli:"host" usage:"specify host" dft:"0.0.0.0"`
	Port uint16 `cli:"port" usage:"specify port" dft:"8080"`
}

Deprecated: Addr is builtin host,port flag

func (Addr) ToString deprecated

func (addr Addr) ToString() string

Deprecated: ToString ...

type AddrWithShort deprecated

type AddrWithShort struct {
	Host string `cli:"H,host" usage:"specify host" dft:"0.0.0.0"`
	Port uint16 `cli:"p,port" usage:"specify port" dft:"8080"`
}

Deprecated: AddrWithShort is builtin host,port flag contains short flag

func (AddrWithShort) ToString deprecated

func (addr AddrWithShort) ToString() string

Deprecated: ToString ...

type ArgvFunc

type ArgvFunc func() interface{}

ArgvFunc ...

type AutoHelper

type AutoHelper interface {
	AutoHelp() bool
}

AutoHelper represents interface for showing help information automatically

type Command

type Command struct {
	Name    string   // Command name
	Aliases []string // Command aliases name
	Desc    string   // Command abstract
	Text    string   // Command detail description
	Hidden  bool     // should hide command in help menus

	// CanSubRoute indicates whether to allow incomplete subcommand routing
	// e.g.
	//
	//	./app cmd1 cmd2
	//
	// Suppose cmd2 not found in sub-commands of cmd1. Command cmd1 would be
	// executed if cmd1.CanSubRoute is true, an error returned otherwise.
	CanSubRoute bool

	// NoHook indicates whether skip hooked functions
	NoHook bool

	// Global indicates whether it's argv object should be used to sub-command
	Global bool

	// functions
	Fn        CommandFunc  // Command handler
	UsageFn   UsageFunc    // Custom usage function
	Argv      ArgvFunc     // Command argument factory function
	NumArg    NumCheckFunc // NumArg check number of args
	NumOption NumCheckFunc // NumOption check num of options

	HTTPRouters []string
	HTTPMethods []string

	// hooks for current command
	OnBefore func(*Context) error
	OnAfter  func(*Context) error

	// hooks for all commands if current command is root command
	OnRootPrepareError func(error) error
	OnRootBefore       func(*Context) error
	OnRootAfter        func(*Context) error
	// contains filtered or unexported fields
}

Command is the top-level instance in command-line app

Example
package main

import (
	"github.com/akeylesslabs/cli"
)

func main() {
	root := &cli.Command{
		Name: "app",
	}

	type childT struct {
		S string `cli:"s" usage:"string flag"`
		B bool   `cli:"b" usage:"boolean flag"`
	}
	root.Register(&cli.Command{
		Name:        "child",
		Aliases:     []string{"sub"},
		Desc:        "child command",
		Text:        "detailed description for command",
		Argv:        func() interface{} { return new(childT) },
		CanSubRoute: true,
		NoHook:      true,
		NumArg:      cli.ExactN(1),
		HTTPRouters: []string{"/v1/child", "/v2/child"},
		HTTPMethods: []string{"GET", "POST"},

		OnRootPrepareError: func(err error) error {
			return err
		},
		OnBefore: func(ctx *cli.Context) error {
			ctx.String("OnBefore\n")
			return nil
		},
		OnAfter: func(ctx *cli.Context) error {
			ctx.String("OnAfter\n")
			return nil
		},
		OnRootBefore: func(ctx *cli.Context) error {
			ctx.String("OnRootBefore\n")
			return nil
		},
		OnRootAfter: func(ctx *cli.Context) error {
			ctx.String("OnRootAfter\n")
			return nil
		},

		Fn: func(ctx *cli.Context) error {
			return nil
		},
	})
}
Output:

func HelpCommand

func HelpCommand(desc string) *Command

HelpCommand returns a buildin help command

func Root

func Root(root *Command, forest ...*CommandTree) *Command

Root registers forest for root and returns root

func (*Command) ChildrenDescriptions

func (cmd *Command) ChildrenDescriptions(prefix, indent string) string

ChildrenDescriptions returns all children's brief infos by one string

func (*Command) IsClient

func (cmd *Command) IsClient() bool

IsClient returns command whether if run as client

func (*Command) IsServer

func (cmd *Command) IsServer() bool

IsServer returns command whether if run as server

func (*Command) ListChildren

func (cmd *Command) ListChildren() []string

ListChildren returns all names of command children

func (*Command) ListenAndServeHTTP

func (cmd *Command) ListenAndServeHTTP(addr string) error

ListenAndServeHTTP set IsServer flag with true and startup http service

func (*Command) Parent

func (cmd *Command) Parent() *Command

Parent returns command's parent

func (*Command) Path

func (cmd *Command) Path() string

Path returns space-separated command full name

func (*Command) RPC

func (cmd *Command) RPC(httpc *http.Client, ctx *Context) error

RPC runs the command from remote

func (*Command) Register

func (cmd *Command) Register(child *Command) *Command

Register registers a child command

func (*Command) RegisterFunc

func (cmd *Command) RegisterFunc(name string, fn CommandFunc, argvFn ArgvFunc) *Command

RegisterFunc registers handler as child command

func (*Command) RegisterHTTP

func (cmd *Command) RegisterHTTP(ctxs ...*Context) error

RegisterHTTP init HTTPRouters for command

func (*Command) RegisterTree

func (cmd *Command) RegisterTree(forest ...*CommandTree)

RegisterTree registers a command tree

func (*Command) Root

func (cmd *Command) Root() *Command

Root returns command's ancestor

func (*Command) Route

func (cmd *Command) Route(router []string) *Command

Route finds command full matching router

func (*Command) Run

func (cmd *Command) Run(args []string) error

Run runs the command with args

func (*Command) RunWith

func (cmd *Command) RunWith(args []string, writer io.Writer, resp http.ResponseWriter, httpMethods ...string) error

RunWith runs the command with args and writer,httpMethods

func (*Command) Serve

func (cmd *Command) Serve(listeners ...net.Listener) (err error)

Serve set IsServer with true and serve http with listeners

func (*Command) ServeHTTP

func (cmd *Command) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements HTTP handler

func (*Command) SetIsServer

func (cmd *Command) SetIsServer(yes bool)

SetIsServer sets command running mode(server or not)

func (*Command) SubRoute

func (cmd *Command) SubRoute(router []string) (*Command, int)

SubRoute finds command partial matching router

func (*Command) Suggestions

func (cmd *Command) Suggestions(path string) []string

Suggestions returns all similar commands

func (*Command) Usage

func (cmd *Command) Usage(ctx *Context) string

Usage returns the usage string of command

type CommandFunc

type CommandFunc func(*Context) error

CommandFunc ...

type CommandTree

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

CommandTree represents a tree of commands

func Tree

func Tree(cmd *Command, forest ...*CommandTree) *CommandTree

Tree creates a CommandTree

type Context

type Context struct {
	HTTPRequest  *http.Request
	HTTPResponse http.ResponseWriter
	// contains filtered or unexported fields
}

Context provides running context

func (*Context) Args

func (ctx *Context) Args() []string

Args returns free args `./app hello world -a=1 abc xyz` will return ["abc" "xyz"]

func (*Context) Argv

func (ctx *Context) Argv() interface{}

Argv returns parsed args object

func (*Context) Color

func (ctx *Context) Color() *color.Color

Color returns color instance

func (*Context) Command

func (ctx *Context) Command() *Command

Command returns current command instance

func (*Context) FormValues

func (ctx *Context) FormValues() url.Values

FormValues returns parsed args as url.Values

func (*Context) GetArgvAt added in v0.0.2

func (ctx *Context) GetArgvAt(argv interface{}, i int) error

GetArgvAt gets the i-th argv object

func (*Context) GetArgvList added in v0.0.2

func (ctx *Context) GetArgvList(curr interface{}, parents ...interface{}) error

GetArgvList gets argv objects

func (*Context) IsSet

func (ctx *Context) IsSet(flag string, aliasFlags ...string) bool

IsSet determins whether `flag` is set

func (*Context) JSON

func (ctx *Context) JSON(obj interface{}) *Context

JSON writes json string of obj to writer

func (*Context) JSONIndent

func (ctx *Context) JSONIndent(obj interface{}, prefix, indent string) *Context

JSONIndent writes pretty json string of obj to writer

func (*Context) JSONIndentln

func (ctx *Context) JSONIndentln(obj interface{}, prefix, indent string) *Context

JSONIndentln writes pretty json string of obj end with "\n" to writer

func (*Context) JSONln

func (ctx *Context) JSONln(obj interface{}) *Context

JSONln writes json string of obj end with "\n" to writer

func (*Context) NArg

func (ctx *Context) NArg() int

NArg returns length of Args

func (*Context) NOpt added in v0.0.2

func (ctx *Context) NOpt() int

NOpt returns num of options

func (*Context) NativeArgs

func (ctx *Context) NativeArgs() []string

NativeArgs returns native args `./app hello world -a --xyz=1` will return ["-a" "--xyz=1"]

func (*Context) Path

func (ctx *Context) Path() string

Path returns full command name `./app hello world -a --xyz=1` will returns "hello world"

func (*Context) RootArgv added in v0.0.2

func (ctx *Context) RootArgv() interface{}

RootArgv returns parsed root args object

func (*Context) Router

func (ctx *Context) Router() []string

Router returns full command name with string array `./app hello world -a --xyz=1` will returns ["hello" "world"]

func (*Context) String

func (ctx *Context) String(format string, args ...interface{}) *Context

String writes formatted string to writer

func (*Context) Usage

func (ctx *Context) Usage() string

Usage returns current command's usage with current context

func (*Context) Write

func (ctx *Context) Write(data []byte) (n int, err error)

Write implements io.Writer

func (*Context) WriteUsage

func (ctx *Context) WriteUsage()

WriteUsage writes usage to writer

func (*Context) Writer

func (ctx *Context) Writer() io.Writer

Writer returns writer

type Counter added in v0.0.2

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

Counter implements counter decoder

func (*Counter) Decode added in v0.0.2

func (c *Counter) Decode(s string) error

Decode decodes counter from string

func (Counter) IsCounter added in v0.0.2

func (c Counter) IsCounter()

IsCounter implements method of interface CounterDecoder

func (Counter) Value added in v0.0.2

func (c Counter) Value() int

Value returns value of counter

type CounterDecoder added in v0.0.2

type CounterDecoder interface {
	Decoder
	IsCounter()
}

CounterDecoder represents an counter decoder

type Decoder

type Decoder interface {
	Decode(s string) error
}

Decoder represents an interface which decodes string

type Encoder

type Encoder interface {
	Encode() string
}

Encoder represents an interface which encodes to string

type FlagParser

type FlagParser interface {
	Parse(s string) error
}

FlagParser represents a parser for parsing flag

Example

This example demonstrates how to use builtin praser(json,jsonfile)

package main

import (
	"io/ioutil"
	"os"

	"github.com/akeylesslabs/cli"
)

type config1 struct {
	A string
	B int
}

type config2 struct {
	C string
	D bool
}

func main() {
	type argT struct {
		Cfg1 config1 `cli:"cfg1" parser:"json"`
		Cfg2 config2 `cli:"cfg2" parser:"jsonfile"`
	}
	jsonfile := "1.json"
	args := []string{"app",
		`--cfg1`, `{"A": "hello", "B": 2}`,
		`--cfg2`, jsonfile,
	}
	ioutil.WriteFile(jsonfile, []byte(`{"C": "world", "D": true}`), 0644)
	defer os.Remove(jsonfile)

	cli.RunWithArgs(new(argT), args, func(ctx *cli.Context) error {
		ctx.JSON(ctx.Argv())
		return nil
	})
}
Output:

{"Cfg1":{"A":"hello","B":2},"Cfg2":{"C":"world","D":true}}

type FlagParserCreator

type FlagParserCreator func(ptr interface{}) FlagParser

FlagParserCreator represents factory function of FlagParser

type Helper

type Helper struct {
	Help bool `cli:"!h,help" usage:"display help information" json:"-"`
}

Helper is builtin Help flag

Example
package main

import (
	"github.com/akeylesslabs/cli"
)

func main() {
	type argT struct {
		cli.Helper
	}
	cli.RunWithArgs(new(argT), []string{"app", "-h"}, func(ctx *cli.Context) error {
		return nil
	})
}
Output:

Options:

  -h, --help   display help information

func (Helper) AutoHelp

func (h Helper) AutoHelp() bool

AutoHelp implements AutoHelper interface

type Helper2 added in v0.0.3

type Helper2 struct {
	Help bool `cli:"!h,help" usage:"Display help information" json:"-"`
}

Helper2 is builtin Help flag

func (Helper2) AutoHelp added in v0.0.3

func (h Helper2) AutoHelp() bool

AutoHelp implements AutoHelper interface

type JSONConfigFileParser added in v0.0.3

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

JSON config file parser

func (JSONConfigFileParser) Parse added in v0.0.3

func (p JSONConfigFileParser) Parse(s string) error

type JSONFileParser

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

JSON file parser

func (JSONFileParser) Parse

func (p JSONFileParser) Parse(s string) error

type JSONParser

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

JSON parser

func (JSONParser) Parse

func (p JSONParser) Parse(s string) error

type NumCheckFunc added in v0.0.2

type NumCheckFunc func(n int) bool

NumCheckFunc represents function type which used to check num of args

func AtLeast added in v0.0.2

func AtLeast(num int) NumCheckFunc

AtLeast returns a NumCheckFunc which checks if a number is greater than or equal to num

func AtMost added in v0.0.2

func AtMost(num int) NumCheckFunc

AtMost returns a NumCheckFunc which checks if a number is less than or equal to num

func ExactN added in v0.0.2

func ExactN(num int) NumCheckFunc

ExactN returns a NumCheckFunc which checks if a number is equal to num

type SliceDecoder added in v0.0.2

type SliceDecoder interface {
	Decoder
	DecodeSlice()
}

SliceDecoder represents an interface which decodes string as slice

type URLParser added in v0.0.2

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

URL parser

func (*URLParser) Parse added in v0.0.2

func (p *URLParser) Parse(s string) error

type UsageFunc added in v0.0.2

type UsageFunc func() string

UsageFunc represents custom function of usage

type UsageStyle

type UsageStyle int32

UsageStyle is style of usage

const (
	// NormalStyle : left-right
	NormalStyle UsageStyle = iota
	// DenseNormalStyle : left-right, too
	DenseNormalStyle
	// ManualStyle : up-down
	ManualStyle
	// DenseManualStyle : up-down, too
	DenseManualStyle
)

func GetUsageStyle

func GetUsageStyle() UsageStyle

GetUsageStyle gets default style

type Validator

type Validator interface {
	Validate(*Context) error
}

Validator validates flag before running command

Jump to

Keyboard shortcuts

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