POSIX-compliant command-line UI (CLI) parser and Hierarchical-configuration operations
Unstable v2: Working in progress, the main API might be stable till v2.1.0
cmdr
is a POSIX-compliant, command-line argument parser library with Golang.
Since v2, our license moved to Apache 2.0.
See the image frames at #1.
There are many dirty codes in the cmdr.v1 which cannot be refactored as well. It prompted we reimplment a new one as v2.
The passing winter, we did rewrite the cmdr.v2 to keep it clean and absorbed in parsing and dispatching.
Some abilities were removed and relayouted to new modules.
That's why the Option Store
has been split as a standalone module hedzr/store[^1].
A faster and colorful slog-like logger has been implemented freshly as hedzr/logg[^3].
hedzr/evendeep[^2] provides a deep fully-functional object copy tool. It helps to deep copy some internal objects easily. It is also ready for you.
hedzr/is[^4] is an environment detecting framework with many out-of-the-box detectors, such as is.InTesting
and is.InDebugging
.
Anyway, the whole supply chain painted:
graph BT
hzis(hedzr/is)-->hzlogg(hedzr/logg/slog)
hzis-->hzdiff(hedzr/evendeep)
hzlogg-->hzdiff
hzerrors(gopkg.in/hedzr/errors.v3)-->hzdiff
hzerrors-->hzstore(hedzr/store)
hzis-->hzstore(hedzr/store)
hzlogg-->hzstore(hedzr/store)
hzdiff-->hzstore(hedzr/store)
hzlogg-->cmdr(hedzr/cmdr/v2)
hzis-->cmdr
hzlogg-->cmdr
hzdiff-->cmdr
hzstore-->cmdr
v2 is in earlier state but the baseline is stable:
Basic command-line arguments parser like POSIX getopt and go stdlib flag.
Short flag, single character or a string here to support golang CLI style
-c1b23zv
= -c 1 -b 23 -z -v
-v -v -v
= -v
(hitCount == 3, hitTitle == 'v')-a 1,2,3 -a 4 -a 5,6
=> []int{1,2,3,4,5,6}-c1
, -c 1
, -c=1
and quoted: -c"1"
, -c'1'
, -c="1"
, -c='1'
, etc.Long flags and aliases
Eventual subcommands: an OnAction
handler can be attached.
Eventual subcommands and flags: PreActions, PostAction, OnMatching, OnMatched, ...,
Auto bind to environment variables, For instance: command line HELP=1 app
= app --help
.
Builtin commands and flags:
--help
, -h
--version
, -V
--verbose
. -v
Help Screen: auto generate and print
Smart suggestions when wrong cmd or flag parsed. Jaro-winkler distance is used.
Loosely parse subcmds and flags:
Can integrate with hedzr/store[^1]
Three kinds of config files are searched and loaded via loaders.NewConfigFileLoader()
:
TODO
[^1]: hedzr/store
is a high-performance configure management library
[^2]: hedzr/evendeep
offers a customizable deepcopy tool to you. There are also deepequal, deepdiff tools in it.
[^3]: hedzr/logg
provides a slog like and colorful logging library
[^4]: hedzr/is
is a basic environ detectors library
More minor details need to be evaluated and reimplemented if it's still meaningful in v2.
v2 is staying in earlier state:
Latest: v2.0.3
Full list: CHANGELOG
A simple cli-app can be:
package main
import (
logz "github.com/hedzr/logg/slog"
"github.com/hedzr/cmdr/v2"
"github.com/hedzr/cmdr/v2/cli"
"github.com/hedzr/cmdr/v2/loaders"
"github.com/hedzr/cmdr/v2/pkg/dir"
"github.com/hedzr/store"
)
func main() {
app := prepareApp()
// // simple run the parser of app and trigger the matched command's action
// _ = app.Run(
// cmdr.WithForceDefaultAction(false), // true for debug in developing time
// )
if err := app.Run(
cmdr.WithStore(store.New()),
cmdr.WithExternalLoaders(
loaders.NewConfigFileLoader(),
loaders.NewEnvVarLoader(),
),
cmdr.WithForceDefaultAction(true), // true for debug in developing time
); err != nil {
logz.Error("Application Error:", "err", err)
}
}
func prepareApp() (app cli.App) {
app = cmdr.New().
Info("demo-app", "0.3.1").
Author("hedzr")
app.AddFlg(func(b cli.FlagBuilder) {
b.Titles("no-default").
Description("disable force default action").
OnMatched(func(f *cli.Flag, position int, hitState *cli.MatchState) (err error) {
app.Store().Set("app.force-default-action", false)
return
})
})
app.AddCmd(func(b cli.CommandBuilder) {
b.Titles("jump").
Description("jump command").
Examples(`jump example`).
Deprecated(`jump is a demo command`).
Hidden(false)
b.AddCmd(func(b cli.CommandBuilder) {
b.Titles("to").
Description("to command").
Examples(``).
Deprecated(`v0.1.1`).
Hidden(false).
OnAction(func(cmd *cli.Command, args []string) (err error) {
app.Store().Set("app.demo.working", dir.GetCurrentDir())
println()
println(dir.GetCurrentDir())
println()
println(app.Store().Dump())
return // handling command action here
})
b.AddFlg(func(b cli.FlagBuilder) {
b.Default(false).
Titles("full", "f").
Description("full command").
Build()
})
})
})
app.AddFlg(func(b cli.FlagBuilder) {
b.Titles("dry-run", "n").
Default(false).
Description("run all but without committing")
})
app.Flg("wet-run", "w").
Default(false).
Description("run all but with committing").
Build() // no matter even if you're adding the duplicated one.
return
}
Thanks to JetBrains for donating product licenses to help develop cmdr
Since v2, our license moved to Apache 2.0.
The v1 keeps under MIT itself.