# README
ecobra
Module eluv-io/ecobra-go
provides packages easing work with cobra:
bflags
: a framework for binding command line flags and parameters to fields of structsapp
: builds onbflags
to build complete - or complex - command line applications in a simple and declarative fashion.params
: add support to some commonly used parameter types like json and os files including piping from/to stdin or stdout
bflags
Package bflags
provides binding of command line flags and parameters to fields of structs through annotations tags.
Tags are specified using 'cmd' followed by either 'flag' or 'arg' and then flags attributes:
flag, name, usage, shorthand, persistent, required, hidden
arg, name, usage, order
usage
: short description of the flag or command line parametershorthand
: a one letter shorthandpersistent
: 'true' to make the flag persistentrequired
: 'true' to make the flag requiredhidden
: 'true' to make the flag hiddenorder
: for command line parameters, an int specifying the order on the command line. If no order is provided the order is taken from fields declaration. Note that the order attribute must be specified on all or none of thearg
fields (you may not have some field with the order specified and some other without)
Not all attributes are required:
type myInput struct {
Ip net.IP `cmd:"flag,ip,node ip,q"`
Path string `cmd:"arg"`
}
The usage
attribute is used by the command line help in the description of the flag.
type JsonToCsvSpec struct {
Template string `cmd:"arg,template,template mapping columns to path in json,0"`
Source *params.PathOrReader `cmd:"arg,source,path to the source file with json,1"`
Output *params.PathOrWriter `cmd:"arg,output,path to the output CSV file,2"`
SheetName string `cmd:"flag,sheet-name,name of the sheet when spreadsheet output"`
WriteColumns bool `cmd:"flag,write-columns,write column names as the first line in csv output"`
IgnoreEmptyRecord bool `cmd:"flag,ignore-empty,do not write empty records"`
}
Bindings are supported for:
- all 'native' types of go (int, float and their flavors, bool, string) and pointer to them
net.IP
,time.Duration
- slices of all the above. They can be comma or space separated on the command line.
- binding to struct or inner structs is also supported, but inner objects have to be initialized pointers (see unit-tests)
bflags
supports binding to custom types through the Flagger
interface (
see flags_custom_test.go for a simple example)
See the bflags doc for a full description and sample.
app
With app
the entire tree of cobra commands is initialized within a struct literal.
The construct looks familiar to cobra users since most fields of the app.Cmd
struct used to construct the top
level app.App
have the same name as their counterpart in cobra.Command
.
func initApp() (*app.App, error) {
spec := app.NewSpec(
[]*app.CmdCategory{
{Name: "base", Title: "start working and configure"},
{Name: "tools", Title: "pre built tools"},
{Name: "others", Title: "others", Default: true},
},
&app.Cmd{
Use: "cli",
Short: "Sample Client",
Long: "A simple command line tool",
PersistentPreRunE: app.CobraFn(initializeApp),
PersistentPostRunE: app.CobraFn(cleanup),
SilenceErrors: true,
SubCommands: []*app.Cmd{
{
Use: "sample test ",
Short: "sample <arg>",
Category: "tools",
Example: "sample the_fox ",
Args: "ExactArgs(1)",
RunE: app.RunFn(execSample),
Input: &InputSample{Port: 8080},
},
},
})
a, err := app.NewApp(spec, nil)
if err != nil {
return nil, err
}
a.SetCommandStart(cmdStart)
a.SetCommandEnd(cmdEnd)
return a, nil
}
Input of command are initialized through the Input
field which will be the second parameter received by the RunE
function.
The RunE
must be a function with two parameters:
- the first parameter of type
app.CmdCtx
is mandatory and can be used as a context to convey key/value pairs - the second parameter is the instance that was defined as
Input
The output of a run function can be:
- a single value - in which case it has to be of type
error
- a pair - in which case the second value has to be of type
error
func execSample(ctx *app.CmdCtx, in *InputSample) (*OutputSample, error) {
}
The app.CmdCtx
context can also be initialized prior to the execution of commands and passed to the application
func main() {
a, err := initApp()
if err != nil {
exit(err)
}
cmdRoot, err := a.Cobra()
if err != nil {
exit(err)
}
// optionally initialize a command context to pass global value to any command
cmdCtx := app.NewCmdCtx()
//cmdCtx.Set("some key", someValue)
bflags.SetCmdCtx(cmdRoot, cmdCtx)
err = cmdRoot.Execute()
exit(err)
}
See app_sample.go for a fully running example (the above).