Merge pull request 'update commands' (#15) from command into master
All checks were successful
Go Action / goaction (push) Successful in 32s

Reviewed-on: #15
This commit was merged in pull request #15.
This commit is contained in:
2026-01-06 16:25:25 +03:00
20 changed files with 414 additions and 165 deletions

15
app.go
View File

@@ -33,7 +33,10 @@ func WithInput(in config.BindProvider) func(*App) {
// WithSkipArgs sets how many arguments are passed. For example, you don't need to pass the name of a single command. // WithSkipArgs sets how many arguments are passed. For example, you don't need to pass the name of a single command.
func WithSkipArgs(l int) func(*App) { func WithSkipArgs(l int) func(*App) {
return WithInput(chain.New(arg.New(arg.WithArgs(os.Args[resolveSkip(l):])), &memory.Default{})) return WithInput(chain.New(
arg.New(arg.WithArgs(os.Args[ResolveSkip(l):])),
&memory.Default{}),
)
} }
// WithExit sets exit callback by default os.Exit. // WithExit sets exit callback by default os.Exit.
@@ -53,7 +56,7 @@ func New(opts ...func(*App)) *App {
out: output.Stdout(), out: output.Stdout(),
exit: os.Exit, exit: os.Exit,
in: chain.New( in: chain.New(
arg.New(arg.WithArgs(os.Args[resolveSkip(0):])), arg.New(arg.WithArgs(os.Args[ResolveSkip(0):])),
&memory.Default{}, &memory.Default{},
), ),
registry: registry.Add, registry: registry.Add,
@@ -134,10 +137,14 @@ func (a *App) list(ctx context.Context) error {
} }
func (a *App) printError(ctx context.Context, err error) { func (a *App) printError(ctx context.Context, err error) {
command.Ansi(ctx, a.in, a.out).Println(ctx, "<error>\n\n ", err, "\n</error>") printErr(ctx, a.in, a.out, err)
} }
func resolveSkip(in int) int { func printErr(ctx context.Context, in config.Provider, out output.Output, err error) {
command.Ansi(ctx, in, out).Printf(ctx, "<error>\n\n %v\n</error>\n", err)
}
func ResolveSkip(in int) int {
res := 2 res := 2
switch { switch {

View File

@@ -6,7 +6,7 @@ import (
"gitoa.ru/go-4devs/config" "gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/console/command" "gitoa.ru/go-4devs/console/command"
"gitoa.ru/go-4devs/console/errors" "gitoa.ru/go-4devs/console/errs"
"gitoa.ru/go-4devs/console/output" "gitoa.ru/go-4devs/console/output"
) )
@@ -115,7 +115,7 @@ func (c *Command) With(opts ...Option) *Command {
// Run run command with input and output. // Run run command with input and output.
func (c *Command) Run(ctx context.Context, in config.Provider, out output.Output) error { func (c *Command) Run(ctx context.Context, in config.Provider, out output.Output) error {
if c.Execute == nil { if c.Execute == nil {
return fmt.Errorf("%w", errors.ErrExecuteNil) return fmt.Errorf("%w", errs.ErrExecuteNil)
} }
if c.Handle != nil { if c.Handle != nil {

View File

@@ -3,11 +3,10 @@ package command
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"gitoa.ru/go-4devs/config" "gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/console/output" "gitoa.ru/go-4devs/console/output"
"gitoa.ru/go-4devs/console/param" "gitoa.ru/go-4devs/console/setting"
) )
type ( type (
@@ -27,17 +26,17 @@ func Configure(fn ConfigureFn) Option {
func Version(in string) Option { func Version(in string) Option {
return func(c *Command) { return func(c *Command) {
c.Params = param.WithVersion(in)(c.Params) c.Setting = setting.WithVersion(in)(c.Setting)
} }
} }
func Hidden(c *Command) { func Hidden(c *Command) {
c.Params = param.Hidden(c.Params) c.Setting = setting.Hidden(c.Setting)
} }
func Help(fn param.HelpFn) Option { func Help(fn setting.HelpFn) Option {
return func(c *Command) { return func(c *Command) {
c.Params = param.WithHelp(fn)(c.Params) c.Setting = setting.WithHelp(fn)(c.Setting)
} }
} }
@@ -61,6 +60,18 @@ func Prepare(fn PrepareFn) Option {
} }
} }
func Usage(fn setting.UsageFn) Option {
return func(c *Command) {
c.Setting = setting.WithUsage(fn)(c.Setting)
}
}
func EmptyUsage(cmd *Command) {
cmd.Setting = setting.WithUsage(func(setting.UData) (string, error) {
return "", nil
})(cmd.Setting)
}
func New(name, desc string, execute ExecuteFn, opts ...Option) Command { func New(name, desc string, execute ExecuteFn, opts ...Option) Command {
cmd := Command{ cmd := Command{
name: name, name: name,
@@ -68,7 +79,7 @@ func New(name, desc string, execute ExecuteFn, opts ...Option) Command {
configure: emptyConfigure, configure: emptyConfigure,
handle: emptyHandle, handle: emptyHandle,
prepare: emptyPrepare, prepare: emptyPrepare,
Params: param.New(param.WithDescription(desc)), Setting: setting.New(setting.WithDescription(desc)),
} }
for _, opt := range opts { for _, opt := range opts {
@@ -79,7 +90,7 @@ func New(name, desc string, execute ExecuteFn, opts ...Option) Command {
} }
type Command struct { type Command struct {
param.Params setting.Setting
name string name string
execute ExecuteFn execute ExecuteFn
@@ -109,13 +120,12 @@ func (c Command) IsZero() bool {
} }
func (c Command) String() string { func (c Command) String() string {
return fmt.Sprintf("command:%v, version:%v", c.Name(), param.Version(c)) return fmt.Sprintf("command:%v, version:%v", c.Name(), setting.Version(c))
} }
func With(parent Command, opts ...Option) Command { func With(parent Command, opts ...Option) Command {
log.Print(parent.Name())
cmd := Command{ cmd := Command{
Params: parent.Params, Setting: parent.Setting,
name: parent.Name(), name: parent.Name(),
execute: parent.Execute, execute: parent.Execute,
configure: parent.Configure, configure: parent.Configure,

View File

@@ -6,8 +6,8 @@ import (
"sort" "sort"
"sync" "sync"
cerr "gitoa.ru/go-4devs/console/errors" "gitoa.ru/go-4devs/console/errs"
"gitoa.ru/go-4devs/console/param" "gitoa.ru/go-4devs/console/setting"
) )
var findCommand = regexp.MustCompile("([^:]+|)") var findCommand = regexp.MustCompile("([^:]+|)")
@@ -71,7 +71,7 @@ func (c *Commands) find(name string) (Command, error) {
} }
for name, idx := range c.names { for name, idx := range c.names {
if cmdRegexp.MatchString(name) && !param.IsHidden(c.cmds[idx]) { if cmdRegexp.MatchString(name) && !setting.IsHidden(c.cmds[idx]) {
findCommands = append(findCommands, c.cmds[idx]) findCommands = append(findCommands, c.cmds[idx])
} }
} }
@@ -86,10 +86,10 @@ func (c *Commands) find(name string) (Command, error) {
names[i] = findCommands[i].Name() names[i] = findCommands[i].Name()
} }
return Command{}, cerr.AlternativesError{Alt: names, Err: cerr.ErrCommandDplicate} return Command{}, errs.AlternativesError{Alt: names, Err: errs.ErrCommandDplicate}
} }
return Command{}, fmt.Errorf("%w", cerr.ErrNotFound) return Command{}, fmt.Errorf("%w", errs.ErrNotFound)
} }
func (c *Commands) set(cmds ...Command) error { func (c *Commands) set(cmds ...Command) error {
@@ -99,7 +99,7 @@ func (c *Commands) set(cmds ...Command) error {
for _, cmd := range cmds { for _, cmd := range cmds {
if cmd.IsZero() { if cmd.IsZero() {
return fmt.Errorf("command:%w", cerr.ErrCommandNil) return fmt.Errorf("command:%w", errs.ErrCommandNil)
} }
if idx, ok := c.names[cmd.Name()]; ok { if idx, ok := c.names[cmd.Name()]; ok {
@@ -122,11 +122,11 @@ func (c *Commands) add(cmds ...Command) error {
for _, cmd := range cmds { for _, cmd := range cmds {
if cmd.IsZero() { if cmd.IsZero() {
return fmt.Errorf("command:%w", cerr.ErrCommandNil) return fmt.Errorf("command:%w", errs.ErrCommandNil)
} }
if _, ok := c.names[cmd.Name()]; ok { if _, ok := c.names[cmd.Name()]; ok {
return fmt.Errorf("command %s:%w", cmd.Name(), cerr.ErrCommandDplicate) return fmt.Errorf("command %s:%w", cmd.Name(), errs.ErrCommandDplicate)
} }
c.names[cmd.Name()] = len(c.cmds) c.names[cmd.Name()] = len(c.cmds)

67
command/dump/reference.go Normal file
View File

@@ -0,0 +1,67 @@
package dump
import (
"context"
"fmt"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition"
"gitoa.ru/go-4devs/config/definition/option"
"gitoa.ru/go-4devs/config/provider/arg"
"gitoa.ru/go-4devs/console/command"
"gitoa.ru/go-4devs/console/errs"
"gitoa.ru/go-4devs/console/internal/registry"
"gitoa.ru/go-4devs/console/output"
)
//go:generate go tool config config:generate
const NameRefernce = "config:dump-reference"
func Command() command.Command {
return command.New(NameRefernce, "dump reference by command", RExecute, command.Configure(RConfigure))
}
func RExecute(ctx context.Context, in config.Provider, out output.Output) error {
provs, ok := in.(config.Providers)
if !ok {
return fmt.Errorf("%w: expect %T got %T", errs.ErrWrongType, (config.Providers)(nil), in)
}
cfg := NewRConfigureConfig(in)
cmd, err := registry.Find(cfg.CommandName(ctx))
if err != nil {
return fmt.Errorf("cmd:%w", err)
}
def := definition.New()
if err := cmd.Configure(ctx, def); err != nil {
return fmt.Errorf("configure:%w", err)
}
prov, err := provs.Provider(cfg.Format(ctx))
if err != nil {
return fmt.Errorf("prov:%w", errs.AlternativesError{Alt: provs.Names(), Err: err})
}
bind, ok := prov.(config.DumpProvider)
if !ok {
return fmt.Errorf("%w: expect config.DunpProvider got %T", errs.ErrWrongType, prov)
}
if err := bind.DumpReference(ctx, out, def); err != nil {
return fmt.Errorf("dump:%w", err)
}
return nil
}
func RConfigure(_ context.Context, def config.Definition) error {
def.Add(
arg.String("command-name", "command name", option.Required),
option.String("format", "format", option.Default(arg.Name)),
)
return nil
}

View File

@@ -0,0 +1,89 @@
// Code generated gitoa.ru/go-4devs/config DO NOT EDIT.
package dump
import (
"context"
"fmt"
"gitoa.ru/go-4devs/config"
)
func WithRConfigureConfigHandle(fn func(context.Context, error)) func(*RConfigureConfig) {
return func(ci *RConfigureConfig) {
ci.handle = fn
}
}
func NewRConfigureConfig(prov config.Provider, opts ...func(*RConfigureConfig)) RConfigureConfig {
i := RConfigureConfig{
Provider: prov,
handle: func(_ context.Context, err error) {
fmt.Printf("RConfigureConfig:%v", err)
},
}
for _, opt := range opts {
opt(&i)
}
return i
}
type RConfigureConfig struct {
config.Provider
handle func(context.Context, error)
}
// readCommandName command name.
func (i RConfigureConfig) readCommandName(ctx context.Context) (v string, e error) {
val, err := i.Value(ctx, "command-name")
if err != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"command-name"}, err)
}
return val.ParseString()
}
// ReadCommandName command name.
func (i RConfigureConfig) ReadCommandName(ctx context.Context) (string, error) {
return i.readCommandName(ctx)
}
// CommandName command name.
func (i RConfigureConfig) CommandName(ctx context.Context) string {
val, err := i.readCommandName(ctx)
if err != nil {
i.handle(ctx, err)
}
return val
}
// readFormat format.
func (i RConfigureConfig) readFormat(ctx context.Context) (v string, e error) {
val, err := i.Value(ctx, "format")
if err != nil {
i.handle(ctx, err)
return "arg", nil
}
return val.ParseString()
}
// ReadFormat format.
func (i RConfigureConfig) ReadFormat(ctx context.Context) (string, error) {
return i.readFormat(ctx)
}
// Format format.
func (i RConfigureConfig) Format(ctx context.Context) string {
val, err := i.readFormat(ctx)
if err != nil {
i.handle(ctx, err)
}
return val
}

View File

@@ -2,6 +2,7 @@ package help
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
"strings" "strings"
@@ -9,15 +10,16 @@ import (
"gitoa.ru/go-4devs/config" "gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition" "gitoa.ru/go-4devs/config/definition"
"gitoa.ru/go-4devs/config/definition/option" "gitoa.ru/go-4devs/config/definition/option"
cparam "gitoa.ru/go-4devs/config/param" "gitoa.ru/go-4devs/config/param"
"gitoa.ru/go-4devs/config/provider/arg" "gitoa.ru/go-4devs/config/provider/arg"
"gitoa.ru/go-4devs/config/validator" "gitoa.ru/go-4devs/config/validator"
"gitoa.ru/go-4devs/config/value" "gitoa.ru/go-4devs/config/value"
"gitoa.ru/go-4devs/console/command" "gitoa.ru/go-4devs/console/command"
"gitoa.ru/go-4devs/console/errs"
"gitoa.ru/go-4devs/console/internal/registry" "gitoa.ru/go-4devs/console/internal/registry"
"gitoa.ru/go-4devs/console/output" "gitoa.ru/go-4devs/console/output"
"gitoa.ru/go-4devs/console/output/descriptor" "gitoa.ru/go-4devs/console/output/descriptor"
"gitoa.ru/go-4devs/console/param" "gitoa.ru/go-4devs/console/setting"
) )
//nolint:gochecknoinits //nolint:gochecknoinits
@@ -91,17 +93,31 @@ func Execute(ctx context.Context, in config.Provider, out output.Output) error {
bin = os.Args[0] bin = os.Args[0]
} }
help, err := param.Help(cmd, param.HelpData(bin, cmd.Name())) help, err := setting.Help(cmd, setting.HelpData(bin, cmd.Name()))
if err != nil { if err != nil {
return fmt.Errorf("create help:%w", err) return fmt.Errorf("create help:%w", err)
} }
hasUsage := true
usage, err := setting.Usage(cmd, setting.UsageData(cmd.Name(), def))
if err != nil {
if !errors.Is(err, errs.ErrNotFound) {
return fmt.Errorf("create usage:%w", err)
}
hasUsage = false
}
derr := des.Command(ctx, out, descriptor.Command{ derr := des.Command(ctx, out, descriptor.Command{
Bin: bin, Bin: bin,
Name: cmd.Name(), Name: cmd.Name(),
Description: param.Description(cmd), Description: setting.Description(cmd),
Help: help, Help: help,
Options: def.With(cparam.New(descriptor.TxtStyle())), Usage: func() (string, bool) {
return usage, hasUsage
},
Options: def.With(param.New(descriptor.TxtStyle())),
}) })
if derr != nil { if derr != nil {
return fmt.Errorf("descriptor help:%w", derr) return fmt.Errorf("descriptor help:%w", derr)
@@ -118,7 +134,7 @@ You can also output the help in other formats by using the <comment>--format</co
To display the list of available commands, please use the <info>list</info> command. To display the list of available commands, please use the <info>list</info> command.
` `
func Help(data param.HData) (string, error) { func Help(data setting.HData) (string, error) {
return fmt.Sprintf(tpl, data.Bin, data.Name), nil return fmt.Sprintf(tpl, data.Bin, data.Name), nil
} }

View File

@@ -8,16 +8,16 @@ import (
"gitoa.ru/go-4devs/config" "gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition" "gitoa.ru/go-4devs/config/definition"
"gitoa.ru/go-4devs/config/definition/option" "gitoa.ru/go-4devs/config/definition/option"
cparam "gitoa.ru/go-4devs/config/param" "gitoa.ru/go-4devs/config/param"
"gitoa.ru/go-4devs/config/provider/arg" "gitoa.ru/go-4devs/config/provider/arg"
"gitoa.ru/go-4devs/config/validator" "gitoa.ru/go-4devs/config/validator"
"gitoa.ru/go-4devs/config/value" "gitoa.ru/go-4devs/config/value"
"gitoa.ru/go-4devs/console/command" "gitoa.ru/go-4devs/console/command"
cerr "gitoa.ru/go-4devs/console/errors" "gitoa.ru/go-4devs/console/errs"
"gitoa.ru/go-4devs/console/internal/registry" "gitoa.ru/go-4devs/console/internal/registry"
"gitoa.ru/go-4devs/console/output" "gitoa.ru/go-4devs/console/output"
"gitoa.ru/go-4devs/console/output/descriptor" "gitoa.ru/go-4devs/console/output/descriptor"
"gitoa.ru/go-4devs/console/param" "gitoa.ru/go-4devs/console/setting"
) )
//nolint:gochecknoinits //nolint:gochecknoinits
@@ -80,7 +80,7 @@ func Execite(ctx context.Context, in config.Provider, out output.Output) error {
cmds := registry.Commands() cmds := registry.Commands()
commands := descriptor.Commands{ commands := descriptor.Commands{
Namespace: ns, Namespace: ns,
Options: def.With(cparam.New(descriptor.TxtStyle())), Options: def.With(param.New(descriptor.TxtStyle())),
} }
groups := make(map[string]*descriptor.NSCommand) groups := make(map[string]*descriptor.NSCommand)
namespaces := make([]string, 0, len(cmds)) namespaces := make([]string, 0, len(cmds))
@@ -92,13 +92,13 @@ func Execite(ctx context.Context, in config.Provider, out output.Output) error {
} }
cmd, _ := registry.Find(name) cmd, _ := registry.Find(name)
if param.IsHidden(cmd) { if setting.IsHidden(cmd) {
continue continue
} }
gn := strings.SplitN(name, ":", defaultLenNamespace) gn := strings.SplitN(name, ":", defaultLenNamespace)
if len(gn) != defaultLenNamespace { if len(gn) != defaultLenNamespace {
empty.Append(cmd.Name(), param.Description(cmd)) empty.Append(cmd.Name(), setting.Description(cmd))
continue continue
} }
@@ -111,7 +111,7 @@ func Execite(ctx context.Context, in config.Provider, out output.Output) error {
namespaces = append(namespaces, gn[0]) namespaces = append(namespaces, gn[0])
} }
groups[gn[0]].Append(name, param.Description(cmd)) groups[gn[0]].Append(name, setting.Description(cmd))
} }
if len(empty.Commands) > 0 { if len(empty.Commands) > 0 {
@@ -123,7 +123,7 @@ func Execite(ctx context.Context, in config.Provider, out output.Output) error {
} }
if ns != "" && len(commands.Commands) == 0 { if ns != "" && len(commands.Commands) == 0 {
return fmt.Errorf("%w: namespace %s", cerr.ErrNotFound, ns) return fmt.Errorf("%w: namespace %s", errs.ErrNotFound, ns)
} }
if err := des.Commands(ctx, out, commands); err != nil { if err := des.Commands(ctx, out, commands); err != nil {

View File

@@ -3,7 +3,7 @@ package list
import ( import (
"fmt" "fmt"
"gitoa.ru/go-4devs/console/param" "gitoa.ru/go-4devs/console/setting"
) )
const tpl = ` const tpl = `
@@ -15,6 +15,6 @@ You can also output the information in other formats by using the <comment>--for
<info>%[1]s %[2]s --format=xml</info> <info>%[1]s %[2]s --format=xml</info>
` `
func Help(data param.HData) (string, error) { func Help(data setting.HData) (string, error) {
return fmt.Sprintf(tpl, data.Bin, data.Name), nil return fmt.Sprintf(tpl, data.Bin, data.Name), nil
} }

View File

@@ -17,7 +17,7 @@ import (
"gitoa.ru/go-4devs/config/value" "gitoa.ru/go-4devs/config/value"
"gitoa.ru/go-4devs/console" "gitoa.ru/go-4devs/console"
"gitoa.ru/go-4devs/console/command" "gitoa.ru/go-4devs/console/command"
cerr "gitoa.ru/go-4devs/console/errors" "gitoa.ru/go-4devs/console/errs"
"gitoa.ru/go-4devs/console/output" "gitoa.ru/go-4devs/console/output"
) )
@@ -82,7 +82,7 @@ func TestRunEmptyExecute(t *testing.T) {
out := output.Stdout() out := output.Stdout()
err := empty.Run(ctx, in, out) err := empty.Run(ctx, in, out)
if !errors.Is(err, cerr.ErrExecuteNil) { if !errors.Is(err, errs.ErrExecuteNil) {
t.Fatalf("expected: %v, got: %v ", cerr.ErrExecuteNil, err) t.Fatalf("expected: %v, got: %v ", errs.ErrExecuteNil, err)
} }
} }

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"log"
"gitoa.ru/go-4devs/config" "gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition" "gitoa.ru/go-4devs/config/definition"
@@ -13,16 +12,16 @@ import (
"gitoa.ru/go-4devs/config/value" "gitoa.ru/go-4devs/config/value"
"gitoa.ru/go-4devs/console/command" "gitoa.ru/go-4devs/console/command"
"gitoa.ru/go-4devs/console/command/help" "gitoa.ru/go-4devs/console/command/help"
cerr "gitoa.ru/go-4devs/console/errors" "gitoa.ru/go-4devs/console/errs"
"gitoa.ru/go-4devs/console/internal/registry" "gitoa.ru/go-4devs/console/internal/registry"
"gitoa.ru/go-4devs/console/output" "gitoa.ru/go-4devs/console/output"
"gitoa.ru/go-4devs/console/param" "gitoa.ru/go-4devs/console/setting"
) )
// Execute the current command with option. // Execute the current command with option.
func Execute(ctx context.Context, cmd command.Command, opts ...func(*App)) { func Execute(ctx context.Context, cmd command.Command, opts ...func(*App)) {
opts = append([]func(*App){WithSkipArgs(1)}, opts...) opts = append([]func(*App){WithSkipArgs(1)}, opts...)
New(opts...).exec(ctx, cmd) New(opts...).exec(ctx, command.With(cmd, command.EmptyUsage))
} }
// Run current command by input and output. // Run current command by input and output.
@@ -38,7 +37,7 @@ func Run(ctx context.Context, cmd command.Command, in config.BindProvider, out o
berr := in.Bind(ctx, config.NewVars(def.Options()...)) berr := in.Bind(ctx, config.NewVars(def.Options()...))
if berr != nil { if berr != nil {
log.Print(berr) printErr(ctx, in, out, berr)
return showHelp(ctx, cmd, in, output.Ansi(out)) return showHelp(ctx, cmd, in, output.Ansi(out))
} }
@@ -46,7 +45,7 @@ func Run(ctx context.Context, cmd command.Command, in config.BindProvider, out o
out = command.Verbose(ctx, in, out) out = command.Verbose(ctx, in, out)
if command.IsShowVersion(ctx, in) { if command.IsShowVersion(ctx, in) {
out.Println(ctx, "command <comment>", cmd.Name(), "</comment> version: <info>", param.Version(cmd), "</info>") out.Println(ctx, "command <comment>", cmd.Name(), "</comment> version: <info>", setting.Version(cmd), "</info>")
return nil return nil
} }
@@ -67,7 +66,7 @@ func showHelp(ctx context.Context, cmd command.Command, in config.Provider, out
arr.SetOption(value.New(cmd.Name()), help.ArgumentCommandName) arr.SetOption(value.New(cmd.Name()), help.ArgumentCommandName)
arr.SetOption(value.New(false), command.OptionHelp) arr.SetOption(value.New(false), command.OptionHelp)
if _, err := registry.Find(cmd.Name()); errors.Is(err, cerr.ErrNotFound) { if _, err := registry.Find(cmd.Name()); errors.Is(err, errs.ErrNotFound) {
_ = registry.Add(cmd) _ = registry.Add(cmd)
} }

View File

@@ -1,4 +1,4 @@
package errors //nolint:revive package errs
import ( import (
"errors" "errors"

7
go.mod
View File

@@ -2,7 +2,7 @@ module gitoa.ru/go-4devs/console
go 1.24.0 go 1.24.0
require gitoa.ru/go-4devs/config v0.0.8 require gitoa.ru/go-4devs/config v0.0.10
require ( require (
golang.org/x/mod v0.31.0 // indirect golang.org/x/mod v0.31.0 // indirect
@@ -10,4 +10,7 @@ require (
golang.org/x/tools v0.40.0 // indirect golang.org/x/tools v0.40.0 // indirect
) )
tool golang.org/x/tools/cmd/stringer tool (
gitoa.ru/go-4devs/config/cmd/config
golang.org/x/tools/cmd/stringer
)

6
go.sum
View File

@@ -1,7 +1,9 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
gitoa.ru/go-4devs/config v0.0.8 h1:o4p8I9jWJMfiFVVKr50IqCGj1fF+8kmSPDkH0deRvn4= gitoa.ru/go-4devs/config v0.0.9 h1:Z43kM6k6ocC2YIRQkELq5zWO9LO8fl1l14RyqjLC4I4=
gitoa.ru/go-4devs/config v0.0.8/go.mod h1:jHKqVafFVW400LC0M4i1ifPapiI9sqpX/QTh+VMadKw= gitoa.ru/go-4devs/config v0.0.9/go.mod h1:cLW1+4E4uM4Pw+z4RuKEKbO1Lz6UTs2b2fTPyeEgTx8=
gitoa.ru/go-4devs/config v0.0.10 h1:NSagD0voj77/IGqRGsbR0DZmDvFcxbx+oRoWQnLnSy4=
gitoa.ru/go-4devs/config v0.0.10/go.mod h1:cLW1+4E4uM4Pw+z4RuKEKbO1Lz6UTs2b2fTPyeEgTx8=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=

View File

@@ -25,6 +25,7 @@ type Command struct {
Bin string Bin string
Name string Name string
Description string Description string
Usage func() (string, bool)
Help string Help string
} }

View File

@@ -12,6 +12,7 @@ import (
"gitoa.ru/go-4devs/config/param" "gitoa.ru/go-4devs/config/param"
"gitoa.ru/go-4devs/config/provider/arg" "gitoa.ru/go-4devs/config/provider/arg"
"gitoa.ru/go-4devs/console/output" "gitoa.ru/go-4devs/console/output"
"gitoa.ru/go-4devs/console/setting"
) )
const ( const (
@@ -22,9 +23,9 @@ const (
//nolint:gochecknoglobals //nolint:gochecknoglobals
var ( var (
txtFunc = template.FuncMap{ txtFunc = template.FuncMap{
"synopsis": txtSynopsis,
"definition": txtDefinition, "definition": txtDefinition,
"help": txtHelp, "help": txtHelp,
"usage": txtUsage,
"commands": txtCommands, "commands": txtCommands,
} }
@@ -34,11 +35,9 @@ var (
{{- if .Description -}} {{- if .Description -}}
<comment>Description:</comment> <comment>Description:</comment>
{{ .Description }} {{ .Description }}
{{ end -}} {{ end -}}
<comment>Usage:</comment> {{- usage . }}
{{ .Name }} {{ synopsis .Options }} {{- definition .Options }}
{{ definition .Options }}
{{- help . }} {{- help . }}
`)) `))
@@ -128,6 +127,23 @@ func txtCommands(cmds []NSCommand) string {
return buf.String() return buf.String()
} }
func txtUsage(cmd Command) string {
if cmd.Usage == nil {
return ""
}
data, has := cmd.Usage()
if has && data == "" {
return ""
}
if data == "" {
data = defaultUsage(setting.UsageData(cmd.Name, cmd.Options))
}
return "\n<comment>Usage:</comment>\n " + data + "\n"
}
func txtHelp(cmd Command) string { func txtHelp(cmd Command) string {
if cmd.Help == "" { if cmd.Help == "" {
return "" return ""
@@ -152,10 +168,12 @@ func txtDefinition(options config.Options) string {
return buf.String() return buf.String()
} }
func txtSynopsis(options config.Options) string { func defaultUsage(data setting.UData) string {
def := arg.NewViews(options, nil) def := arg.NewViews(data.Options, nil)
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString(data.Name)
buf.WriteString(" ")
if len(def.Options()) > 0 { if len(def.Options()) > 0 {
buf.WriteString("[options] ") buf.WriteString("[options] ")

View File

@@ -1,90 +0,0 @@
package param
import (
"fmt"
cerr "gitoa.ru/go-4devs/console/errors"
)
type key uint8
const (
paramHidden key = iota + 1
paramDescription
paramVerssion
paramHelp
)
const (
defaultVersion = "undefined"
)
func IsHidden(in Params) bool {
data, ok := Bool(in, paramHidden)
return ok && data
}
func Hidden(in Params) Params {
return in.With(paramHidden, true)
}
func Description(in Params) string {
data, _ := String(in, paramDescription)
return data
}
func WithDescription(desc string) Option {
return func(p Params) Params {
return p.With(paramDescription, desc)
}
}
func Version(in Params) string {
if data, ok := String(in, paramVerssion); ok {
return data
}
return defaultVersion
}
func WithVersion(in string) Option {
return func(p Params) Params {
return p.With(paramVerssion, in)
}
}
func HelpData(bin, name string) HData {
return HData{
Bin: bin,
Name: name,
}
}
type HData struct {
Bin string
Name string
}
type HelpFn func(data HData) (string, error)
func WithHelp(fn HelpFn) Option {
return func(p Params) Params {
return p.With(paramHelp, fn)
}
}
func Help(in Params, data HData) (string, error) {
fn, ok := in.Param(paramHelp)
if !ok {
return "", nil
}
hfn, fok := fn.(HelpFn)
if !fok {
return "", fmt.Errorf("%w: expect:%T, got:%T", cerr.ErrWrongType, (HelpFn)(nil), fn)
}
return hfn(data)
}

View File

@@ -1,6 +1,6 @@
package param package setting
func Bool(in Params, key any) (bool, bool) { func Bool(in Setting, key any) (bool, bool) {
data, ok := in.Param(key) data, ok := in.Param(key)
if !ok { if !ok {
return false, false return false, false
@@ -11,7 +11,7 @@ func Bool(in Params, key any) (bool, bool) {
return res, ok return res, ok
} }
func String(in Params, key any) (string, bool) { func String(in Setting, key any) (string, bool) {
data, ok := in.Param(key) data, ok := in.Param(key)
if !ok { if !ok {
return "", false return "", false

127
setting/keys.go Normal file
View File

@@ -0,0 +1,127 @@
package setting
import (
"fmt"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/console/errs"
)
type key uint8
const (
paramHidden key = iota + 1
paramDescription
paramVerssion
paramHelp
paramUsage
)
const (
defaultVersion = "undefined"
)
func IsHidden(in Setting) bool {
data, ok := Bool(in, paramHidden)
return ok && data
}
func Hidden(in Setting) Setting {
return in.With(paramHidden, true)
}
func Description(in Setting) string {
data, _ := String(in, paramDescription)
return data
}
func WithDescription(desc string) Option {
return func(p Setting) Setting {
return p.With(paramDescription, desc)
}
}
func Version(in Setting) string {
if data, ok := String(in, paramVerssion); ok {
return data
}
return defaultVersion
}
func WithVersion(in string) Option {
return func(p Setting) Setting {
return p.With(paramVerssion, in)
}
}
func HelpData(bin, name string) HData {
return HData{
Bin: bin,
Name: name,
}
}
type HData struct {
Bin string
Name string
}
type HelpFn func(data HData) (string, error)
func WithHelp(fn HelpFn) Option {
return func(p Setting) Setting {
return p.With(paramHelp, fn)
}
}
func Help(in Setting, data HData) (string, error) {
fn, ok := in.Param(paramHelp)
if !ok {
return "", nil
}
hfn, fok := fn.(HelpFn)
if !fok {
return "", fmt.Errorf("%w: expect:func(data HData) (string, error), got:%T", errs.ErrWrongType, fn)
}
return hfn(data)
}
func UsageData(name string, opts config.Options) UData {
return UData{
Options: opts,
Name: name,
}
}
type UData struct {
config.Options
Name string
}
type UsageFn func(data UData) (string, error)
func WithUsage(fn UsageFn) Option {
return func(p Setting) Setting {
return p.With(paramUsage, fn)
}
}
func Usage(in Setting, data UData) (string, error) {
fn, ok := in.Param(paramUsage)
if !ok {
return "", fmt.Errorf("%w", errs.ErrNotFound)
}
ufn, ok := fn.(UsageFn)
if !ok {
return "", fmt.Errorf("%w: expect: func(data Udata) (string, error), got:%T", errs.ErrWrongType, fn)
}
return ufn(data)
}

View File

@@ -1,10 +1,10 @@
package param package setting
//nolint:gochecknoglobals //nolint:gochecknoglobals
var eparam = empty{} var eparam = empty{}
func New(opts ...Option) Params { func New(opts ...Option) Setting {
var param Params var param Setting
param = eparam param = eparam
for _, opt := range opts { for _, opt := range opts {
@@ -14,12 +14,12 @@ func New(opts ...Option) Params {
return param return param
} }
type Params interface { type Setting interface {
Param(key any) (any, bool) Param(key any) (any, bool)
With(key, val any) Params With(key, val any) Setting
} }
type Option func(Params) Params type Option func(Setting) Setting
type empty struct{} type empty struct{}
@@ -27,7 +27,7 @@ func (e empty) Param(any) (any, bool) {
return nil, false return nil, false
} }
func (e empty) With(key, val any) Params { func (e empty) With(key, val any) Setting {
return data{ return data{
parent: e, parent: e,
key: key, key: key,
@@ -36,7 +36,7 @@ func (e empty) With(key, val any) Params {
} }
type data struct { type data struct {
parent Params parent Setting
key, val any key, val any
} }
@@ -48,7 +48,7 @@ func (d data) Param(key any) (any, bool) {
return d.parent.Param(key) return d.parent.Param(key)
} }
func (d data) With(key, val any) Params { func (d data) With(key, val any) Setting {
return data{ return data{
parent: d, parent: d,
key: key, key: key,