github.com/conseweb/cli

Golang cli,best command line interface for go (go cli)


License
MIT
Install
go get github.com/conseweb/cli

Documentation

Command line interface License

License

The MIT License (MIT)

中文文档

Install

go get github.com/mkideal/cli

Screenshot

screenshot screenshot2

Features

  • Simplest, fast to learn.
  • Safety. Support type check, range check, and custom validate function.
  • Based on golang tag. Support four tags: cli,usage,dft, name.
  • Support default value and required declaration.
  • Support multiple flag name for same argument.
  • Support command tree.
  • Support command suggestion
  • Support HTTP router
  • Support struct field
  • Support -F<value> flag format
  • Support separated flags and arguments by --
  • Distinguish flags and arguments - app cmd --flag -b=c arg1 arg2
  • Support array flag - -F v1 -F v2 or -Fv1 -Fv2
  • Support map flag - -F k1=v1 -F k2=v2 or -F<k1=v1> -F<k2=v2>

TODOs

  • Support command completion
  • Support Before/After hooks
  • Support expr for dft tag

Getting started

Just run it!

package main

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

type argT struct {
    Help bool   `cli:"h,help" usage:"display help information"`
    Name string `cli:"name" usage:"your name" dft:"world"`
    Age  uint8  `cli:"a,age" usage:"your age" dft:"100"`
}

func main() {
    cli.Run(&argT{}, func(ctx *cli.Context) error {
        argv := ctx.Argv().(*argT)
        if argv.Help {
            ctx.String(ctx.Usage())
        } else {
            ctx.String("Hello, %s! Your age is %d?\n", argv.Name, argv.Age)
        }
        return nil
    })
}

Type these in terminal

$> go build -o hello
$> ./hello
Hello, world! Your age is 100?
$> ./hello --name=clipher -a 9
Hello, clipher! Your age is 9?
$> ./hello -h

Try

$> ./hello -a 256

Tags

cli

cli tag supports short format and long format, e.g.

Help    bool    `cli:"h,help"`
Version string  `cli:"version"`
Port    int     `cli:"p"`
XYZ     bool    `cli:"x,y,z,xyz,XYZ"` 

The argument is required if cli tag has prefix *, e.g.

Required string `cli:"*required"`

The argument is marked as force flag if cli tag has prefix !, e.g

Help bool `cli:"!h,help"`

Don't validate flags if force assigned with true.

usage

usage tag describes the argument. If the argument is required, description has prefix * while show usage(* is red on unix-like os).

dft

dft tag specifies argument's default value.You can specify ENV as default value. e.g.

Port   int    `cli:"p,port" usage:"http port" dft:"8080"` 
Home   string `cli:"home" usage:"home dir" dft:"$HOME"`
GoRoot string `cli:"goroot" usage:"go root dir" dft:"$GOROOT"`

name

name tag give a name for show.

Command

Command struct

type Command struct {
    Name        string
    Desc        string
    Text        string
    Fn          CommandFunc
    Argv        ArgvFunc
    CanSubRoute bool
    HTTPRouters []string
    HTTPMethods []string
    ...
}

Command tree

Method 1: Construct command tree

// Tree creates a CommandTree
func Tree(cmd *Command, forest ...*CommandTree) *CommandTree

// Root registers forest for root and return root
func Root(root *Command, forest ...*CommandTree) *Command
func log(ctx *cli.Context) error {
    ctx.String("path: `%s`\n", ctx.Path())
    return nil
}
var (
    cmd1  = &cli.Command{Name: "cmd1", Fn: log}
    cmd11 = &cli.Command{Name: "cmd11", Fn: log}
    cmd12 = &cli.Command{Name: "cmd12", Fn: log}

    cmd2   = &cli.Command{Name: "cmd2", Fn: log}
    cmd21  = &cli.Command{Name: "cmd21", Fn: log}
    cmd22  = &cli.Command{Name: "cmd22", Fn: log}
    cmd221 = &cli.Command{Name: "cmd221", Fn: log}
    cmd222 = &cli.Command{Name: "cmd222", Fn: log}
    cmd223 = &cli.Command{Name: "cmd223", Fn: log}
)

root := cli.Root(
    &cli.Command{Name: "tree"},
    cli.Tree(cmd1,
        cli.Tree(cmd11),
        cli.Tree(cmd12),
    ),
    cli.Tree(cmd2,
        cli.Tree(cmd21),
        cli.Tree(cmd22,
            cli.Tree(cmd221),
            cli.Tree(cmd222),
            cli.Tree(cmd223),
        ),
    ),
)

Method 2: Register child command

Command registers child command using Register method or RegisterFunc method.

func (cmd *Command) Register(*Command) *Command
func (cmd *Command) RegisterFunc(string, CommandFunc, ArgvFunc) *Command
root
├── sub1
│   ├── sub11
│   └── sub12
└── sub2
var root = &cli.Command{}

var sub1 = root.Register(&cli.Command{
    Name: "sub1",
    Fn: func(ctx *cli.Context) error {
        //do something
    },
})
var sub11 = sub1.Register(&cli.Command{
    Name: "sub11",
    Fn: func(ctx *cli.Context) error {
        //do something
    },
})
var sub12 = sub1.Register(&cli.Command{
    Name: "sub12",
    Fn: func(ctx *cli.Context) error {
        //do something
    },
})

var sub2 = root.Register(&cli.Command{
    Name: "sub2",
    Fn: func(ctx *cli.Context) error {
        //do something
    },
})

HTTP router

Context implements ServeHTTP method.

func (ctx *Context) ServeHTTP(w http.ResponseWriter, r *http.Request)

Command has two http properties: HTTPMethods and HTTPRouters.

HTTPRouters []string
HTTPMethods []string

Each command have one default router: slashes-separated router path of command. e.g. command $app sub1 sub11 has default router /sub1/sub11. The HTTPRouters property will add some new extra routers, it would'nt replace the default router. The HTTPMethods is a string array. It will handle any method if HTTPMethods is empty.

var help = &cli.Command{
    Name:        "help",
    Desc:        "display help",
    CanSubRoute: true,
    HTTPRouters: []string{"/v1/help", "/v2/help"},
    HTTPMethods: []string{"GET", "POST"},

    Fn: cli.HelpCommandFn,
}

NOTE: Must call root command's RegisterHTTP method if you want to use custom HTTPRouters

...
if err := ctx.Command().Root().RegisterHTTP(ctx); err != nil {
    return err
}
return http.ListenAndServe(addr, ctx.Command().Root())
...

See example HTTP.

RPC

See example RPC.

Examples

Who use

External tools

  • goplus - generate go application skeleton