hzDocs

From struct-value and Tag

BuildFrom Example

You can build command system by kinds of forms:

  • traditional stream calls (app.Cmd("verbose", "v").Action(onVerbose))
  • concise modes by [Create] and cmd/xxcmd.go
  • use [Create.BuildFrom] to build cmdsys from a struct value via [App.FromStruct], see example #example_Create_buildFromStructValue

Getting started from New or Create function.

Builing command hierarchy is a new feature since cmdr.v2 v2.1.36.

This feature is quite like kong, but a little bit rough. Nonetheless, its abilities are still fully by supplying With() and Action() methods to a stuct.

./examples/tiny/struct/main.go
package main
 
import (
	"context"
	"os"
 
	"github.com/hedzr/cmdr/v2"
	logz "github.com/hedzr/logg/slog"
)
 
const (
	appName = "struct"
	desc    = `struct buidler version of tiny app.`
	version = cmdr.Version
	author  = `The Example Authors`
)
 
func main() {
 
func main() {
	var Root struct {
		Remove struct {
			Full struct {
				NoForce bool `desc:"DON'T Force removal."`
			} `desc:"remove full of files"`
 
			Force     bool `help:"Force removal."`
			Recursive bool `help:"Recursively remove files."`
 
			Paths []string `arg:"" name:"path" help:"Paths to remove." type:"path"`
		} `title:"remove" shorts:"rm" cmd:"" help:"Remove files."`
 
		List struct {
			Paths []string `arg:"" optional:"" name:"path" help:"Paths to list." type:"path"`
		} `title:"list" shorts:"ls" cmd:"" help:"List paths."`
	}
 
	app := cmdr.Create(appName, version, author, desc).
		// WithAdders(cmd.Commands...).
		BuildFrom(&Root)
 
	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)
	}
}

Commonly these keys are useful:

  • title, name: Long title field
  • shorts, short: comma-separated Short Titles. First of them will be used for Flag.Short field, else for ExtraShorts
  • aliases, alias: Long Alias titles
  • desc, help: Desc field for displaying in help screen
  • required:Only for Flag, the Flag.Required field
  • the others will be ignored

Run the app like this,

$ go run -v -tags hzstudio,hzwork,vscode ./examples/tiny/struct
struct v2.1.36 ~ Copyright © 2025 by The Example Authors ~ All Rights Reserved.
 
Usage:
 
  $ struct  [Options...][files...]
 
Description:
 
  struct buidler version of tiny app.
 
Commands:
 
  rm,remove                                   Remove files.
  ls,list                                     List paths.
 
Global Flags:
 
  [Misc]
    -h, --help,--info,--usage                 Show this help screen (-?) [Env: HELP] (Default: false)
 
Type '-h'/'-?' or '--help' to get this help screen (273x25/46).
More: '-D'/'--debug', '-V'/'--version', '-#'/'--build-info', '--no-color'...

The help screen of rm full subcommand is:

$ go run ./examples/tiny/struct rm full
struct v2.1.36 ~ Copyright © 2025 by The Example Authors ~ All Rights Reserved.
 
Usage:
 
  $ struct  remove full [Options...][files...]
 
Description:
 
  remove full of files
 
Flags:
 
  -no-force, --no-force                       DON'T Force removal. (Default: false)
 
Parent Flags:
(Cmd{'remove'}):
  -force, --force                             Force removal. (Default: false)
  -recursive, --recursive                     Recursively remove files. (Default: false)
  -path, --path                               Paths to remove. (Default: [])
 
Global Flags:
 
  [Misc]
    -h, --help,--info,--usage                 Show this help screen (-?) [Env: HELP] (Default: false)
 
Type '-h'/'-?' or '--help' to get this help screen (304x25/46).
More: '-D'/'--debug', '-V'/'--version', '-#'/'--build-info', '--no-color'...

Using With() method

Defining each structs is useful to controlling them.

The following structs can be passing into cmdr by Create().BuildFrom(R{}).

type A struct {
	D
	F1 int
	F2 string
}
type B struct {
	F2 int
	F3 string
}
type C struct {
	F3 bool
	F4 string
}
type D struct {
	E
	FromNowOn F
	F3        bool
	F4        string
}
type E struct {
	F3 bool `title:"f3" shorts:"ff" alias:"f3ff" desc:"A flag for demo" required:"true"`
	F4 string
}
type F struct {
	F5 uint
	F6 byte
}
 
type R struct {
	b   bool // unexported values ignored
	Int int  `cmdr:"-"` // ignored
	A   `title:"a-cmd" shorts:"a,a1,a2" alias:"a1-cmd,a2-cmd" desc:"A command for demo" required:"true"`
	B
	C
	F1 int
	F2 string
}
 
func (A) With(cb cli.CommandBuilder) {
	// customize for A command, for instance: fb.ExtraShorts("ff")
	logz.Info(".   - A.With() invoked.", "cmdbuilder", cb)
}
func (A) F1With(fb cli.FlagBuilder) {
	// customize for A.F1 flag, for instance: fb.ExtraShorts("ff")
	logz.Info(".   - A.F1With() invoked.", "flgbuilder", fb)
}
 
func (s *F) Inc() {
	s.F5++
}

Here the With() methods allow you customize a command or flag with traditional way.

Using Action() method

In above example, we could add Action() method to E and F to give the invoking OnAction callback.

// Action method will be called if end-user type subcmd for it (like `app a d e --f3`).
func (E) Action(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
	logz.Info(".   - E.Action() invoked.", "cmd", cmd, "args", args)
	_, err = cmd.App().DoBuiltinAction(ctx, cli.ActionDefault, stringArrayToAnyArray(args)...)
	return
}
 
// Action method will be called if end-user type subcmd for it (like `app a d f --f5=7`).
func (s F) Action(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
	(&s).Inc()
	logz.Info(".   - F.Action() invoked.", "cmd", cmd, "args", args, "F5", s.F5)
	_, err = cmd.App().DoBuiltinAction(ctx, cli.ActionDefault, stringArrayToAnyArray(args)...)
	return
}
 
func stringArrayToAnyArray(args []string) (ret []any) {
	for _, it := range args {
		ret = append(ret, it)
	}
	return
}

So, there it is.

Specifying default value

To specify default value to a flag, you could setup them in struct-value.

func TestStructBuilder_FromStruct(t *testing.T) {
	// New will initialize appS{} struct and make a new
	// rootCommand object into it.
	var w cli.Runner // an empty dummy runner for testing
	a := New(w).Info("demo-app", "0.3.1").Author("hedzr")
	app := a.(*appS)
	// logz.SetLevel(logz.DebugLevel)
 
	// FromStruct assumes creating a command system from RootCommand.Cmd
	// since a bracketed longTitle "(...)" passed.
	b := app.FromStruct(R{
		F2: "/tmp/value",
	})
	b.Build()
 
	assertEqual(t, int32(0), app.inCmd)
	assertEqual(t, int32(0), app.inFlg)
 
	root := app.root.Cmd.(*cli.CmdS)
	assertEqual(t, "/tmp/value", root.Flags()[1].DefaultValue())
 
  //...
}

The future

We could iterate this feature in recent versions to integrated with blueprint app.

:end:

How is this guide?

Edit on GitHub

Last updated on

On this page