Clif
Another CLI framework for Go. It works on my machine.
Install / Use
/learn @ukautz/ClifREADME

Command line interface framework
Go framework for rapid command line application development.
Example

package main
import "gopkg.in/ukautz/clif.v1"
func main() {
clif.New("My App", "1.0.0", "An example application").
New("hello", "The obligatory hello world", func(out clif.Output) {
out.Printf("Hello World\n")
}).
Run()
}
<!-- TOC START --> <!-- TOC END -->
Install
$ go get gopkg.in/ukautz/clif.v1
Getting started
On the one side, CLIF's builder-like API can be easily used for rapid development of small, single purpose tools. On the other side, CLIF is designed with complex console applications in mind.
Commands
Commands must have a unique name and can have additional arguments and options.
cmd1 := clif.NewCommand("name", "A description", callBackFunction)
cmd2 := clif.NewCommand("other", "Another description", callBackFunction2)
The name is used from the command line to call the command:
$ ./app name
$ ./app other
Callback functions
Callback functions can have arbitrary parameters. CLIF uses a small, built-in (signatur) injection container which allows you to register any kind of object (struct or interface) beforehand.
So you can register any object (interface{}, struct{} .. and anything else, see below) in your bootstrap and then "require" those instances by simply putting them in the command callback signature:
// Some type definition
type MyFoo struct {
X int
}
func main() {
// init cli
cli := clif.New("My App", "1.0.0", "An example application")
// register object instance with container
foo := &MyFoo{X: 123}
cli.Register(foo)
// Create command with callback using the peviously registered instance
cli.NewCommand("foo", "Call foo", func (foo *MyFoo) {
// do something with foo
})
cli.Run()
}
Using interfaces is possible as well, but a bit less elegant:
// Some interface
type MyBar interface {
Bar() string
}
// Some type
type MyFoo struct {
}
// implement interface
func (m *MyFoo) Bar() string {
return "bar"
}
func main() {
// init cli
cli := clif.New("My App", "1.0.0", "An example application")
// create object, which implements MyBar:
foo := &MyFoo{}
t := reflect.TypeOf((*MyBar)(nil)).Elem()
cli.RegisterAs(t.String(), foo)
// Register command with callback using the type
cli.NewCommand("bar", "Call bar", func (bar MyBar) {
// do something with bar
})
cli.Run()
}
Named
Everything works great if you only have a single instance of any object of a specific type.
However, if you need more than one instance (which might often be the case for primitive
types, such as int or string) you can use named registering:
// Register abitrary objects under unique name
cli.RegisterNamed("foo", new(MyFoo)).
RegisterNamed("bar", 123).
RegisterNamed("baz", "bla")
// Register command with callback named container
cli.NewCommand("bar", "Call bar", func (named clif.NamedParameters) {
asMap := map[string]interface{}(named)
fmt.Println(asMap["baz"].(string))
})
Note: If you want to use the named feature, you cannot Register() any NamedParameters
instance yourself, since "normally" registered objects are evaluated before named.
Default objects
CLIF pre-populates the dependency container with a couple of built-in objects:
- The
Output(formatted output helper, see below), egfunc (out clif.Output) { .. } - The
Input(input helper, see below), egfunc (in clif.Input) { .. } - The
*Cliinstance itself, egfunc (c *clif.Cli) { .. } - The current
*Commandinstance, egfunc (o *clif.Command) { .. }
Arguments and Options
CLIF can deal with arguments and options. The difference being:
- Arguments come after the command name. They are identified by their position.
- Options have no fixed position. They are identified by their
--opt-name(or alias, eg-O)
Of course you can use arguments and options at the same time..
Arguments
Arguments are additional command line parameters which come after the command name itself.
cmd := clif.NewCommand("hello", "A description", callBackFunction)
.NewArgument("name", "Name for greeting", "", true, false)
arg := cmd.NewAgument("other", "Something ..", "default", false, true)
cmd.AddArgument(arg)
Arguments consist of a name, a description, an optional default value a required flag and a multiple flag.
$ ./my-app hello the-name other1 other2 other3
# ^ ^ ^ ^ ^
# | | | | |
# | | | | third "other" arg
# | | | second "other" arg
# | | first "other" arg
# | the "name" arg
# command name
Position of arguments matters. Make sure you add them in the right order. And: required arguments must come before optional arguments (makes sense, right?). There can be only one multiple argument at all and, of course, it must be the last (think: variadic).
You can access the arguments by injecting the command instance *clif.Command into the callback and calling the Argument() method. You can choose to interpret the argument as String(), Int(), Float(), Bool(), Time() or Json(). Multiple arguments can be accessed with Strings(), Ints() .. and so on. Count() gives the amount of (provided) multiple arguments and Provided() returns bool for optional arguments. Please see parameter.go for more.
func callbackFunctionI(c *clif.Command) {
// a single
name := c.Argument("name").String()
// a multiple
others := c.Argument("other").Strings()
// .. do something ..
}
Options
Options have no fixed position, meaning ./app --foo --bar and ./app --bar --foo are equivalent. Options are referenced by their name (eg --name) or alias (eg -n). Unless the option is a flag (see below) it must have a value. The value must immediately follow the option. Valid forms are: --name value, --name=value, -n value and -n=value.
Options must come before the command, unless they use the = separator. For example: ./app command --opt value is valid, ./app --opt=value command is valid but ./app --opt value command is not valid (since it becomes impossible to distinguish between command and value).
cmd := clif.NewCommand("hello", "A description", callBackFunction)
.NewOption("name", "n", "Name for greeting", "", true, false)
arg := cmd.NewOption("other", "O", "Something ..", "default", false, true)
cmd.AddOption(arg)
Now:
$ ./my-app hello --other bar -n Me -O foo
# ^ ^ ^
# | | |
# | | second other opt with value
# | name opt with value
# first other opt with value
You can access options the same way as arguments, just use Option() instead.
func callbackFunctionI(c *clif.Command) {
name := c.Option("name").String()
others := c.Option("other").Strings()
// .. do something ..
}
Flags
There is a special kind of option, which does not expect a parameter: the flag. As options, their position is arbitrary.
// shorthand
flag := clif.NewFlag("my-flag", "f", "Something ..", false)
// which would just do:
flag = clif.NewOption("my-flag", "f", "Something ..", "", false, false).IsFlag()
cmd := clif.NewCommand("hello", "A description", callBackFunction).AddOption(flag)
When using the option, you dont need to (nor can you) provide an argument:
$ ./my-app hello --my-flag
You want to use Bool() to check if a flag is provided:
func callbackFunctionI(c *clif.Command) {
if c.Option("my-flag").Bool() {
// ..
}
}
Validation & (Parsing | Transformation)
You can validate/parse/transform the input using the Parse attribute of options or arguments. It can be (later on)
set using the SetParse() method:
// Validation example
arg := clif.NewArgument("my-int", "An integer", "", true, false).
SetParse(func(name, value string) (string, error) {
if _, err := strconv.Atoi(value); err != nil {
return "", fmt.Errorf("Oops: %s is not an integer: %s", name, err)
} else {
return value, nil
}
})
// Transformation example
opt := clif.NewOption("clie
