hzDocs

Shared App

multiple app instances

多个 app 实例

自 cmdr v2.1.11 起,我们支持声明多个 app 实例,彼此互不相干。

examples/shared/main.go
package main
 
import (
	"context"
	"fmt"
	"io"
	"os"
 
	"github.com/hedzr/cmdr/v2"
	"github.com/hedzr/cmdr/v2/cli"
	"github.com/hedzr/cmdr/v2/examples/cmd"
	"github.com/hedzr/cmdr/v2/examples/dyncmd"
 
	"github.com/hedzr/is/dir"
	logz "github.com/hedzr/logg/slog"
	"gopkg.in/hedzr/errors.v3"
)
 
const (
	appName = "shared-app"
	desc    = `shared-app version of tiny app.`
	version = cmdr.Version
	author  = `The Example Authors`
)
 
func main() {
	// ctx, cancel := context.WithCancel(context.Background())
	// defer cancel()
 
	ctx := context.Background()
 
	// declare a shared app.
	//
	// In a shared app, cmdr.App() doesn't point to the first
	// created app instance by you any more.
	// So you must manage each app instance by youself, and
	// some cmdr package-level functions can not be used,
	// such as cmdr.App() and its derivants.
	//
	//
	ctx = context.WithValue(ctx, "shared.cmdr.app", true)
 
	app := cmdr.Create(appName, version, author, desc).
		WithAdders(cmd.Commands...).
		OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
			// In a shared app, cmdr.App() doesn't point to the first
			// created app instance by you any more.
			fmt.Printf("app.name = %s\n", cmdr.App().Name())
			fmt.Printf("app.unique = %v\n", cmdr.App()) // return an uncertain app object
			app := cmd.Root().App()                     // this is the real app object associate with current RootCommand
			fmt.Printf("app = %v\n", app)
			return
		}).
		Build()
 
	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)
	}
}
 
var Commands = []cli.CmdAdder{
	jumpCmd{},
	wrongCmd{},
	invokeCmd{},
	presetCmd{},
}
 
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).
		OnEvaluateSubCommandsFromConfig().
		// 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.Store(), cmd.Store() == cmdr.Store()
					cmd.Set().Set("tiny3.working", dir.GetCurrentDir())
					println()
					println("dir:", cmd.Set().WithPrefix("tiny3").MustString("working"))
 
					cs := cmdr.Store().WithPrefix("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()
				})
		})
}
 
type wrongCmd struct{}
 
func (wrongCmd) Add(app cli.App) {
	app.Cmd("wrong").
		Description("a wrong command to return error for testing").
		Group("Test").
		// cmdline `FORCE_RUN=1 go run ./tiny wrong -d 8s` to verify this command to see the returned application error.
		OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
			dur := cmd.Store().MustDuration("duration")
			println("the duration is:", dur.String())
 
			ec := errors.New()
			defer ec.Defer(&err) // store the collected errors in native err and return it
			ec.Attach(io.ErrClosedPipe, errors.New("something's wrong"), os.ErrPermission)
			// see the application error by running `go run ./tiny/tiny/main.go wrong`.
			return
		}).
		Build()
}
 
type invokeCmd struct{}
 
func (invokeCmd) Add(app cli.App) {
	app.Cmd("invoke").Description(`test invoke feature`).
		With(func(b cli.CommandBuilder) {
			b.Cmd("shell").Description(`invoke shell cmd`).InvokeShell(`ls -la`).UseShell("/bin/bash").OnAction(nil).Build()
			b.Cmd("proc").Description(`invoke gui program`).InvokeProc(`say "hello, world!"`).OnAction(nil).Build()
		})
}
 
type presetCmd struct{}
 
func (presetCmd) Add(app cli.App) {
	app.Cmd("preset", "p").
		Description("preset command to inject into user input").
		With(func(b cli.CommandBuilder) {
			b.Flg("preset", "p").
				Default(false).
				Description("preset arg").
				Build()
			b.Cmd("cmd", "c").Description("inject `-pv` into user input cmdline").
				PresetCmdLines(`-pv`).
				OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
					_, err = app.DoBuiltinAction(ctx, cli.ActionDefault)
					return
				}).Build()
		})
}

一个 shard.cmdr.app 实例不再被登记到全局的 unique app object 中,因此很多顶级命令将不再可用。 例如 cmdr.App() 及其衍生函数是为了从 unique app object 获得 app 实例对象,然后据此取得一系列的关联对象。 但现在它们不再有意义了。这时候,你必须通过 cmd.App()OnAction 中取得你的 app 对象,然后自行提取关联对象。

额外的话题

How is this guide?

Edit on GitHub

Last updated on

On this page