f64f7fa04e
Clone commands before wrapping Before hooks, export ErrLifecycleNotInContext and ErrNoDependencies, add Run helper, split command.go and deps.go. Co-authored-by: Cursor <cursoragent@cursor.com>
105 lines
2.2 KiB
Go
105 lines
2.2 KiB
Go
package appcli
|
|
|
|
import (
|
|
"context"
|
|
|
|
"gitea.auvem.com/go-toolkit/app"
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
// NewCommand wraps cmd with dependency loading in Before. The input command is
|
|
// not mutated; a shallow copy is returned.
|
|
func NewCommand(cmdcfg *cli.Command, depfn ...DepFn) *cli.Command {
|
|
if len(depfn) == 0 {
|
|
return cmdcfg
|
|
}
|
|
|
|
cmd := cloneCommand(cmdcfg)
|
|
originalBefore := cmdcfg.Before
|
|
cmd.Before = func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
|
|
if originalBefore != nil {
|
|
var err error
|
|
ctx, err = originalBefore(ctx, cmd)
|
|
if err != nil {
|
|
return ctx, err
|
|
}
|
|
}
|
|
|
|
lifecycle := app.LifecycleFromContext(ctx)
|
|
if lifecycle == nil {
|
|
return ctx, ErrLifecycleNotInContext
|
|
}
|
|
|
|
deps := make([]*app.Module, 0)
|
|
for _, fn := range depfn {
|
|
if fn == nil {
|
|
continue
|
|
}
|
|
|
|
modules, err := fn(lifecycle, cmd)
|
|
if err != nil {
|
|
return ctx, err
|
|
}
|
|
deps = append(deps, modules...)
|
|
}
|
|
|
|
if len(deps) > 0 {
|
|
if err := lifecycle.Require(deps...); err != nil {
|
|
return ctx, err
|
|
}
|
|
}
|
|
|
|
return ctx, nil
|
|
}
|
|
|
|
return cmd
|
|
}
|
|
|
|
// NewRootCommand wraps a root command, adds a global --verbose / -v flag, and
|
|
// applies dependency loading via [NewCommand].
|
|
func NewRootCommand(cmdcfg *cli.Command, depfn ...DepFn) *cli.Command {
|
|
root := cloneCommand(cmdcfg)
|
|
if !hasFlag(root, "verbose") {
|
|
root.Flags = append(root.Flags, &cli.BoolFlag{
|
|
Name: "verbose",
|
|
Aliases: []string{"v"},
|
|
Usage: "Enable verbose output",
|
|
})
|
|
}
|
|
return NewCommand(root, depfn...)
|
|
}
|
|
|
|
// VerboseFromCommand reports whether --verbose was set on the command or an ancestor.
|
|
func VerboseFromCommand(cmd *cli.Command) bool {
|
|
if cmd == nil {
|
|
return false
|
|
}
|
|
return cmd.Bool("verbose")
|
|
}
|
|
|
|
func cloneCommand(src *cli.Command) *cli.Command {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
dup := *src
|
|
if len(src.Flags) > 0 {
|
|
dup.Flags = append([]cli.Flag(nil), src.Flags...)
|
|
}
|
|
if len(src.Commands) > 0 {
|
|
dup.Commands = append([]*cli.Command(nil), src.Commands...)
|
|
}
|
|
if len(src.Arguments) > 0 {
|
|
dup.Arguments = append([]cli.Argument(nil), src.Arguments...)
|
|
}
|
|
return &dup
|
|
}
|
|
|
|
func hasFlag(cmd *cli.Command, name string) bool {
|
|
for _, f := range cmd.Flags {
|
|
if bf, ok := f.(*cli.BoolFlag); ok && bf.Name == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|