update dump args
Some checks failed
Go Action / goaction (pull_request) Failing after 1m53s

This commit is contained in:
2026-01-05 14:13:11 +03:00
parent e48a9b29d8
commit 5577600f95
8 changed files with 72 additions and 258 deletions

21
app.go
View File

@@ -28,7 +28,7 @@ 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.
func WithSkipArgs(l int) func(*App) {
return WithInput(chain.New(arg.New(arg.WithSkip(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.
@@ -43,7 +43,7 @@ func New(opts ...func(*App)) *App {
app := &App{
out: output.Stdout(),
exit: os.Exit,
in: chain.New(arg.New(arg.WithSkip(0)), &memory.Default{}),
in: chain.New(arg.New(arg.WithArgs(os.Args[resolveSkip(0):])), &memory.Default{}),
}
for _, opt := range opts {
@@ -125,3 +125,20 @@ func (a *App) list(ctx context.Context) error {
func (a *App) printError(ctx context.Context, err error) {
ansi(ctx, a.in, a.out).Println(ctx, "<error>\n\n ", err, "\n</error>")
}
func resolveSkip(in int) int {
res := 2
switch {
case in > 0 && len(os.Args) > in:
res = in
case in > 0:
res = len(os.Args)
case len(os.Args) == 1:
res = 1
case len(os.Args) > 1 && os.Args[1][0] == '-':
res = 1
}
return res
}

View File

@@ -38,7 +38,7 @@ func ExampleNew_help() {
// --group-test-string[=GROUP-TEST-STRING] test group string [default:group string default value]
// --log-{service}-level[=LOG-{SERVICE}-LEVEL] service level [default:debug]
// --bool test bool option
// --duration[=DURATION] test duration with default [default: 1s]
// --duration[=DURATION] test duration with default
// --ansi Do not ask any interactive question
// -V, --version Display this application version
// -h, --help Display this help message

4
go.mod
View File

@@ -2,7 +2,9 @@ module gitoa.ru/go-4devs/console
go 1.24.0
require gitoa.ru/go-4devs/config v0.0.7
require gitoa.ru/go-4devs/config v0.0.8
replace gitoa.ru/go-4devs/config => ../config
require (
golang.org/x/mod v0.31.0 // indirect

4
go.sum
View File

@@ -1,12 +1,8 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
gitoa.ru/go-4devs/config v0.0.7 h1:8q6axRNLgXE5dYQd8Jbh9j+STqevbibVyvwrtsuHpZk=
gitoa.ru/go-4devs/config v0.0.7/go.mod h1:UINWnObZA0nLiJro+TtavUBBvN0cSt17aRHOk20pP74=
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/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=

View File

@@ -9,6 +9,7 @@ import (
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition"
"gitoa.ru/go-4devs/config/definition/option"
"gitoa.ru/go-4devs/config/param"
"gitoa.ru/go-4devs/config/provider/arg"
"gitoa.ru/go-4devs/config/validator"
"gitoa.ru/go-4devs/config/value"
@@ -70,7 +71,7 @@ To display the list of available commands, please use the <info>list</info> comm
Name: cmd.Name,
Description: cmd.Description,
Help: cmd.Help,
Definition: descriptor.NewDefinition(config.NewVars(def.Options()...).Variables()),
Options: def.With(param.New(descriptor.TxtStyle())),
})
if derr != nil {
return fmt.Errorf("descriptor help:%w", derr)

View File

@@ -8,6 +8,7 @@ import (
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition"
"gitoa.ru/go-4devs/config/definition/option"
"gitoa.ru/go-4devs/config/param"
"gitoa.ru/go-4devs/config/provider/arg"
"gitoa.ru/go-4devs/config/validator"
"gitoa.ru/go-4devs/config/value"
@@ -72,7 +73,7 @@ func executeList(ctx context.Context, in config.Provider, out output.Output) err
cmds := Commands()
commands := descriptor.Commands{
Namespace: ns,
Definition: descriptor.NewDefinition(config.NewVars(definition.New(Default()...).Options()...).Variables()),
Options: definition.New(Default()...).With(param.New(descriptor.TxtStyle())),
}
groups := make(map[string]*descriptor.NSCommand)
namespaces := make([]string, 0, len(cmds))

View File

@@ -3,13 +3,9 @@ package descriptor
import (
"context"
"errors"
"sort"
"strings"
"sync"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition/option"
"gitoa.ru/go-4devs/config/provider/arg"
"gitoa.ru/go-4devs/console/output"
)
@@ -24,16 +20,18 @@ var (
)
type Command struct {
config.Options
Bin string
Name string
Description string
Help string
Definition Definition
}
type Commands struct {
config.Options
Namespace string
Definition Definition
Commands []NSCommand
}
@@ -46,65 +44,6 @@ func (n *NSCommand) Append(name, desc string) {
n.Commands = append(n.Commands, ShortCommand{Name: name, Description: desc})
}
func NewDefinition(opts []config.Variable) Definition {
type data struct {
name string
pos uint64
opt config.Variable
}
posArgs := make([]data, 0, len(opts))
posOpt := make([]data, 0, len(opts))
for _, opt := range opts {
pos, ok := arg.ParamArgument(opt)
if !ok {
pos, _ = option.DataPosition(opt)
posOpt = append(posOpt, data{pos: pos, opt: opt})
continue
}
posArgs = append(posArgs, data{name: strings.Join(opt.Key(), "."), pos: pos, opt: opt})
}
sort.Slice(posArgs, func(i, j int) bool {
return posArgs[i].pos > posArgs[j].pos && posArgs[i].name > posArgs[j].name
})
sort.Slice(posOpt, func(i, j int) bool {
return posOpt[i].pos < posOpt[j].pos
})
args := make([]config.Variable, len(posArgs))
for idx := range posArgs {
args[idx] = posArgs[idx].opt
}
options := make([]config.Variable, len(posOpt))
for idx := range posOpt {
options[idx] = posOpt[idx].opt
}
return Definition{
options: options,
args: args,
}
}
type Definition struct {
args []config.Variable
options []config.Variable
}
func (d Definition) Arguments() []config.Variable {
return d.args
}
func (d Definition) Options() []config.Variable {
return d.options
}
type ShortCommand struct {
Name string
Description string

View File

@@ -4,21 +4,18 @@ import (
"bytes"
"context"
"fmt"
"strconv"
"strings"
"text/template"
"time"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition/option"
"gitoa.ru/go-4devs/config/param"
"gitoa.ru/go-4devs/config/value"
"gitoa.ru/go-4devs/config/provider/arg"
"gitoa.ru/go-4devs/console/output"
)
const (
defaultSpace = 2
infoLen = 13
dashDelimiter = "-"
)
@@ -40,8 +37,8 @@ var (
{{ end -}}
<comment>Usage:</comment>
{{ .Name }} {{ synopsis .Definition }}
{{- definition .Definition }}
{{ .Name }} {{ synopsis .Options }}
{{ definition .Options }}
{{- help . }}
`))
@@ -49,11 +46,24 @@ var (
Funcs(txtFunc).
Parse(`<comment>Usage:</comment>
command [options] [arguments]
{{- definition .Definition }}
{{ definition .Options }}
{{- commands .Commands -}}
`))
)
func TxtStyle() param.Option {
return arg.WithStyle(
arg.Style{
Start: "<comment>",
End: "</comment>",
},
arg.Style{
Start: "<info>",
End: "</info>",
},
)
}
type txt struct{}
func (t *txt) Command(ctx context.Context, out output.Output, cmd Command) error {
@@ -82,54 +92,6 @@ func (t *txt) Commands(ctx context.Context, out output.Output, cmds Commands) er
return nil
}
func txtDefaultArray(val config.Value) string {
var st any
err := val.Unmarshal(&st)
if err != nil {
return ""
}
return fmt.Sprintf("%v", st)
}
//nolint:cyclop
func txtDefault(val config.Value, vr config.Variable) []byte {
var buf bytes.Buffer
buf.WriteString("<comment> [default: ")
dataType := param.Type(vr)
if option.IsSlice(vr) {
buf.WriteString(txtDefaultArray(val))
} else {
switch dataType.(type) {
case int:
buf.WriteString(strconv.Itoa(val.Int()))
case int64:
buf.WriteString(strconv.FormatInt(val.Int64(), 10))
case uint:
buf.WriteString(strconv.FormatUint(uint64(val.Uint()), 10))
case uint64:
buf.WriteString(strconv.FormatUint(val.Uint64(), 10))
case float64:
buf.WriteString(strconv.FormatFloat(val.Float64(), 'g', -1, 64))
case time.Duration:
buf.WriteString(val.Duration().String())
case time.Time:
buf.WriteString(val.Time().Format(time.RFC3339))
case string:
buf.WriteString(val.String())
default:
buf.WriteString(fmt.Sprint(val.Any()))
}
}
buf.WriteString("]</comment>")
return buf.Bytes()
}
func txtCommands(cmds []NSCommand) string {
width := commandsTotalWidth(cmds)
showNS := len(cmds) > 1
@@ -181,95 +143,20 @@ func txtHelp(cmd Command) string {
return buf.String()
}
func txtDefinitionOption(maxLen int, opts ...config.Variable) string {
buf := bytes.Buffer{}
buf.WriteString("\n\n<comment>Options:</comment>\n")
for _, opt := range opts {
if option.IsHidden(opt) {
continue
}
var op bytes.Buffer
op.WriteString(" <info>")
if short, ok := option.ParamShort(opt); ok {
op.WriteString("-")
op.WriteString(short)
op.WriteString(", ")
} else {
op.WriteString(" ")
}
op.WriteString("--")
op.WriteString(strings.Join(opt.Key(), dashDelimiter))
if !option.IsBool(opt) {
if !option.IsRequired(opt) {
op.WriteString("[")
}
op.WriteString("=")
op.WriteString(strings.ToUpper(strings.Join(opt.Key(), dashDelimiter)))
if !option.IsRequired(opt) {
op.WriteString("]")
}
}
op.WriteString("</info>")
buf.Write(op.Bytes())
buf.WriteString(strings.Repeat(" ", maxLen+17-op.Len()))
buf.WriteString(option.DataDescription(opt))
if data, ok := option.DataDefaut(opt); ok {
buf.Write(txtDefault(value.New(data), opt))
}
if option.IsSlice(opt) {
buf.WriteString("<comment> (multiple values allowed)</comment>")
}
buf.WriteString("\n")
}
return buf.String()
}
func txtDefinition(def Definition) string {
width := totalWidth(def)
func txtDefinition(options config.Options) string {
var buf bytes.Buffer
if args := def.Arguments(); len(args) > 0 {
buf.WriteString("\n\n<comment>Arguments:</comment>\n")
for _, arg := range args {
var ab bytes.Buffer
ab.WriteString(" <info>")
ab.WriteString(strings.Join(arg.Key(), dashDelimiter))
ab.WriteString("</info>")
ab.WriteString(strings.Repeat(" ", width+infoLen+defaultSpace-ab.Len()))
buf.Write(ab.Bytes())
buf.WriteString(option.DataDescription(arg))
if data, ok := option.DataDefaut(arg); ok {
buf.Write(txtDefault(value.New(data), arg))
}
}
}
if opts := def.Options(); len(opts) > 0 {
buf.WriteString(txtDefinitionOption(width, opts...))
err := arg.NewDump().Reference(&buf, options)
if err != nil {
return err.Error()
}
return buf.String()
}
func txtSynopsis(def Definition) string {
func txtSynopsis(options config.Options) string {
def := arg.NewViews(options, nil)
var buf bytes.Buffer
if len(def.Options()) > 0 {
@@ -294,7 +181,7 @@ func txtSynopsis(def Definition) string {
}
buf.WriteString("<")
buf.WriteString(strings.Join(arg.Key(), dashDelimiter))
buf.WriteString(arg.Name(dashDelimiter))
buf.WriteString(">")
if option.IsSlice(arg) {
@@ -320,32 +207,3 @@ func commandsTotalWidth(cmds []NSCommand) int {
return width
}
//nolint:mnd
func totalWidth(def Definition) int {
var width int
for _, arg := range def.Arguments() {
if l := len(strings.Join(arg.Key(), dashDelimiter)); l > width {
width = l
}
}
for _, opt := range def.Options() {
current := len(strings.Join(opt.Key(), dashDelimiter)) + 6
if !option.IsBool(opt) {
current = current*2 + 1
}
if _, ok := option.DataDefaut(opt); ok {
current += 2
}
if current > width {
width = current
}
}
return width
}