README
¶
Nagini - Fluent and generic API for Cobra
Nagini wraps the famous Cobra CLI library with a fluent API and Go generics.
It supports slice values (comma-separated values) and arbitrary parsing.
It optionally binds flags to the Viper configuration library.
Installation
go get github.com/neiser/go-nagini
Example Usage
Simple command with generic, string-like flag and boolean flag
Showing examples/simple/main.go:
package main
import (
"log"
"github.com/neiser/go-nagini/command"
"github.com/neiser/go-nagini/flag"
)
type (
Wand string
)
func main() {
var (
myName string
favoriteWand Wand = "elder"
iAmVoldemort bool
)
_ = command.New().
Flag(flag.String(&myName, flag.NotEmpty), flag.RegisterOptions{
Name: "my-name",
Required: true,
}).
Flag(flag.String(&favoriteWand, flag.NotEmptyTrimmed), flag.RegisterOptions{
Name: "favorite-wand",
Usage: "Specify magic wand",
}).
Flag(flag.Bool(&iAmVoldemort), flag.RegisterOptions{
Name: "i-am-voldemort",
}).
Run(func() error {
if iAmVoldemort {
return command.WithExitCodeError{ExitCode: 66}
}
log.Printf("I'm %s and my favorite wand is '%s'", myName, favoriteWand)
return nil
}).
Execute()
}
Run with
go run ./examples/simple --my-name Harry
Command with slice flag
Showing examples/slice/main.go:
package main
import (
"log"
"strconv"
"github.com/neiser/go-nagini/command"
"github.com/neiser/go-nagini/flag"
)
func main() {
var (
someInts []int
)
_ = command.New().
Flag(flag.Slice(&someInts, flag.ParseSliceOf(strconv.Atoi)), flag.RegisterOptions{
Name: "some-ints",
Required: true,
}).
Run(func() error {
log.Printf("Got integers: '%v'", someInts)
return nil
}).
Execute()
}
Run with
go run ./examples/slice --some-ints 5,6,7
Adding subcommands with fluent description
Showing examples/subcommand/main.go:
package main
import (
"errors"
"log"
"github.com/neiser/go-nagini/command"
"github.com/neiser/go-nagini/flag"
)
var ErrCannotUseMagic = errors.New("cannot use magic")
func main() {
var (
useMagic bool
)
_ = command.New().
Flag(flag.Bool(&useMagic), flag.RegisterOptions{
Name: "use-magic",
Usage: "Use some magic, c'mon",
Persistent: true,
}).
AddCommands(
command.New().
Use("muggle").
Short("A person which cannot use magic").
Run(func() error {
if useMagic {
return command.WithExitCodeError{
ExitCode: 21,
Wrapped: ErrCannotUseMagic,
}
}
return nil
}),
command.New().
Use("wizard").
Short("A person which may use magic").
Run(func() error {
if useMagic {
log.Printf("Abracadabra!")
}
return nil
}),
).
AddPersistentPreRun(func() error {
log.Printf("Will always run!")
return nil
}).
AddPersistentPreRun(func() error {
log.Printf("Will also run!")
return nil
}).
Execute()
}
Run with
go run ./examples/subcommand wizard --use-magic
go run ./examples/subcommand muggle --use-magic
Binding a flag to Viper, flag value takes precedence over Viper
Showing examples/viper/main.go:
package main
import (
"errors"
"log"
"github.com/neiser/go-nagini/command"
"github.com/neiser/go-nagini/flag"
"github.com/neiser/go-nagini/flag/binding"
"github.com/spf13/viper"
)
func main() {
viper.AutomaticEnv() // tell Viper to read env
var (
favoriteHouse = "Hufflepuff"
isEvil = false
)
_ = command.New().
Flag(
binding.Viper{
Value: flag.String(&favoriteHouse, flag.NotEmptyTrimmed),
ConfigKey: "FAVORITE_HOUSE",
},
flag.RegisterOptions{
Name: "house",
},
).
Flag(
binding.Viper{
Value: flag.Bool(&isEvil),
ConfigKey: "IS_EVIL",
},
flag.RegisterOptions{
Shorthand: "e",
Persistent: true,
},
).
Run(func() error {
prefix := "Favorite"
if isEvil {
prefix = "Evil favorite"
}
log.Printf("%s house is %s", prefix, favoriteHouse)
return nil
}).
AddCommands(command.New().Use("secret-chamber").Run(func() error {
if !isEvil {
return errors.New("only evil persons can enter")
}
return nil
})).
Execute()
}
Run with
IS_EVIL=true FAVORITE_HOUSE=Slytherin go run ./examples/viper
or
FAVORITE_HOUSE=Slytherin go run ./examples/viper --house Hufflepuff
or
IS_EVIL=true go run ./examples/viper secret-chamber
Marking groups of flags
Showing examples/mark/main.go:
package main
import (
"log"
"github.com/neiser/go-nagini/command"
"github.com/neiser/go-nagini/flag"
)
func main() {
var (
name = "Harry"
iAmVoldemort bool
)
_ = command.New().
Flag(flag.String(&name, flag.NotEmpty), flag.RegisterOptions{
Name: "name",
}).
Flag(flag.String(&name, flag.NotEmpty), flag.RegisterOptions{
Name: "nickname",
}).
Flag(flag.Bool(&iAmVoldemort), flag.RegisterOptions{
Name: "i-am-voldemort",
}).
MarkFlagsMutuallyExclusive(&name, &iAmVoldemort).
Run(func() error {
switch {
case iAmVoldemort:
log.Print("My name is Voldemort!")
case name != "":
log.Printf("My name is %s", name)
}
return nil
}).
Execute()
}
Run with
go run ./examples/mark --i-am-voldemort
or (will fail)
go run ./examples/mark --i-am-voldemort --name "Harry"
Implementing flag.Value for VerboseLevel
Showing examples/verbose/main.go:
package main
import (
"fmt"
"log"
"strconv"
"github.com/neiser/go-nagini/command"
"github.com/neiser/go-nagini/flag"
)
type VerboseLevel int
func (v *VerboseLevel) String() string {
return fmt.Sprintf("%d", *v)
}
func (v *VerboseLevel) Set(s string) error {
if s == "true" {
*v++
return nil
}
val, err := strconv.Atoi(s)
if err != nil {
return fmt.Errorf("cannot convert: %w", err)
}
*v = VerboseLevel(val)
return nil
}
func (v *VerboseLevel) Type() string {
return "int"
}
//nolint:ireturn
func (v *VerboseLevel) Target() any {
return v
}
func (v *VerboseLevel) IsBoolFlag() bool {
return true
}
func main() {
var (
verboseLevel VerboseLevel
enableDebug bool
)
_ = command.New().
Flag(&verboseLevel, flag.RegisterOptions{Name: "verbose", Shorthand: "v"}).
Flag(flag.Bool(&enableDebug), flag.RegisterOptions{Name: "debug"}).
MarkFlagsMutuallyExclusive(&enableDebug, &verboseLevel).
Run(func() error {
log.Printf("Verbose level is %d\n", verboseLevel)
return nil
}).
Execute()
}
Run with
go run ./examples/verbose -vvv
Development and Contributions
Install the provided pre-commit hooks with
pre-commit install
This library only exposes some limited feature set of Cobra.
Please open an issue if you really miss something which fits well into this library.
Otherwise, you can always modify the embedded *cobra.Command directly as a workaround.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package command constructs cobra.Command instances fluently starting from New.
|
Package command constructs cobra.Command instances fluently starting from New. |
|
Package flag adds flags to commands.
|
Package flag adds flags to commands. |
|
binding
Package binding binds flags to external configuration systems, such as Viper.
|
Package binding binds flags to external configuration systems, such as Viper. |
Click to show internal directories.
Click to hide internal directories.