Concise Version
Best practise for project layout
Small app with a better project layout
In last examples, we show you how to create a tiny CLI app from scratch, by cmdr.
It has a clear code ochestration step by step, including how to build a cli.App object, to add commands and to add flags into it, and so on.
And, in a truth, there is another way to clearify all of above, by Create().
The Builder interface returned by Create() supports these methods: WithAdders(...) can receive a set of subcommand definitions, and adds bunch of them into app.RootCommand.
Tip
Create() is our recommendation when using cmdr.v2.
package main
import (
"context"
"os"
"github.com/hedzr/cmdr/v2"
"github.com/hedzr/cmdr/v2/examples/cmd"
"github.com/hedzr/cmdr/v2/pkg/logz"
)
const (
appName = "concise"
desc = `concise version of tiny app.`
version = cmdr.Version
author = `The Example Authors`
)
func main() {
app := cmdr.Create(appName, version, author, desc).
WithAdders(cmd.Commands...).
Build()
ctx := context.Background()
if err := app.Run(ctx); err != nil {
logz.ErrorContext(ctx, "Application Error:", "err", err) // stacktrace if in debug mode/build
os.Exit(app.SuggestRetCode())
} else if rc := app.SuggestRetCode(); rc != 0 {
os.Exit(rc)
}
}package cmd
import (
"github.com/hedzr/cmdr/v2/cli"
)
var Commands = []cli.CmdAdder{
jumpCmd{},
// wrongCmd{},
}package cmd
import (
"context"
"github.com/hedzr/cmdr/v2"
"github.com/hedzr/cmdr/v2/cli"
"github.com/hedzr/cmdr/v2/examples/dyncmd"
"github.com/hedzr/cmdr/v2/pkg/dir"
logz "github.com/hedzr/logg/slog"
)
type jumpCmd struct{}
func (jumpCmd) Add(app cli.App) {
app.Cmd("jump").
Description("jump command").
Examples(`jump example`). // {{.AppName}}, {{.AppVersion}}, {{.DadCommands}}, {{.Commands}}, ...
Deprecated(`v1.1.0`).
Group("Test").
// Group(cli.UnsortedGroup).
// Hidden(false).
// OnEvaluateSubCommands(dyncmd.OnEvalJumpSubCommands).
// Both With(cb) and Build() to end a building sequence
With(func(b cli.CommandBuilder) {
b.Cmd("to").
Description("to command").
Examples(``).
Deprecated(`v0.1.1`).
OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
// cmd.Set() == cmdr.Set(), cmd.Store() == cmdr.Store(cmd.GetDottedPath())
_, _ = cmd.Set().Set("tiny3.working", dir.GetCurrentDir())
println()
println("dir:", cmd.Set().WithPrefix("tiny3").MustString("working"))
cs := cmd.Set().WithPrefix(cli.CommandsStoreKey, "jump.to")
if cs.MustBool("full") {
println()
println(cmd.Set().Dump())
}
cs2 := cmd.Store()
if cs2.MustBool("full") != cs.MustBool("full") {
logz.Panic("a bug found")
}
app.SetSuggestRetCode(1) // ret code must be in 0-255
return // handling command action here
}).
With(func(b cli.CommandBuilder) {
b.Flg("full", "f").
Default(false).
Description("full command").
Build()
})
})
}In the subpackage cmd/, you can split each subcommand definition within a standalone file.
All of these commands will be collected as var Commands, in cmd/all.go, then passed into Create(...).WithAdders(cmd.Commands...).
So we think it is a better style.
Although it does not make the codes less.
With Store, and loading external sources
This topic will be discussed at next two sections.
The forecast is, by using loaders.Create instead of cmdr.Create, you will get it done.
What is Next?
How is this guide?
Last updated on