Skip to content

Commit

Permalink
feat(gnoland): Improve gnoland config/secrets command description (g…
Browse files Browse the repository at this point in the history
  • Loading branch information
gfanton authored and linhpn99 committed Aug 20, 2024
1 parent ddf5e4a commit d1a7c4d
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 0 deletions.
13 changes: 13 additions & 0 deletions gno.land/cmd/gnoland/config_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ func newConfigGetCmd(io commands.IO) *commands.Command {
},
)

// Add subcommand helpers
helperGen := metadataHelperGenerator{
MetaUpdate: func(meta *commands.Metadata, inputType string) {
meta.ShortUsage = fmt.Sprintf("config get %s <%s>", meta.Name, inputType)
},
TagNameSelector: "json",
TreeDisplay: true,
}
subs := generateSubCommandHelper(helperGen, config.Config{}, func(_ context.Context, args []string) error {
return execConfigGet(cfg, io, args)
})

cmd.AddSubCommands(subs...)
return cmd
}

Expand Down
127 changes: 127 additions & 0 deletions gno.land/cmd/gnoland/config_help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package main

import (
"context"
"fmt"
"reflect"
"strings"
"unicode"

"github.com/gnolang/gno/tm2/pkg/commands"
)

type metadataHelperGenerator struct {
// Optional callback to edit metadata
MetaUpdate func(meta *commands.Metadata, inputType string)
// Tag to select for name, if empty will use the field Name
TagNameSelector string
// Will display description with tree representation
TreeDisplay bool
}

// generateSubCommandHelper generates subcommands based on `s` structure fields and their respective tag descriptions
func generateSubCommandHelper(gen metadataHelperGenerator, s any, exec commands.ExecMethod) []*commands.Command {
rv := reflect.ValueOf(s)
metas := gen.generateFields(rv, "", 0)

cmds := make([]*commands.Command, len(metas))
for i := 0; i < len(metas); i++ {
meta := metas[i]
exec := func(ctx context.Context, args []string) error {
args = append([]string{meta.Name}, args...)
return exec(ctx, args)
}
cmds[i] = commands.NewCommand(meta, nil, exec)
}

return cmds
}

func (g *metadataHelperGenerator) generateFields(rv reflect.Value, parent string, depth int) []commands.Metadata {
if parent != "" {
parent += "."
}

// Unwrap pointer if needed
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
// Create a new non-nil instance of the original type that was nil
rv = reflect.New(rv.Type().Elem())
}
rv = rv.Elem() // Dereference to struct value
}

metas := []commands.Metadata{}
if rv.Kind() != reflect.Struct {
return metas
}

rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
field := rt.Field(i)
if !field.IsExported() {
continue
}

fieldValue := rv.Field(i)
name := field.Name
// Get JSON tag name
if g.TagNameSelector != "" {
name, _, _ = strings.Cut(field.Tag.Get(g.TagNameSelector), ",")
if name == "" || name == "-" {
continue
}
}

// Recursive call for nested struct
var childs []commands.Metadata
if k := fieldValue.Kind(); k == reflect.Ptr || k == reflect.Struct {
childs = g.generateFields(fieldValue, name, depth+1)
}

// Generate metadata
var meta commands.Metadata

// Name
meta.Name = parent + name

// Create a tree-like display to see nested field
if g.TreeDisplay && depth > 0 {
meta.ShortHelp += strings.Repeat(" ", depth*2)
if i == rv.NumField()-1 {
meta.ShortHelp += "└─"
} else {
meta.ShortHelp += "├─"
}
}
meta.ShortHelp += fmt.Sprintf("<%s>", field.Type)

// Get Short/Long Help Message from comment tag
comment := field.Tag.Get("comment")
comment = strings.TrimFunc(comment, func(r rune) bool {
return unicode.IsSpace(r) || r == '#'
})

if comment != "" {
// Use the first line as short help
meta.ShortHelp += " "
meta.ShortHelp += strings.Split(comment, "\n")[0]

// Display full comment as Long Help
meta.LongHelp = comment
} else {
// If the comment is empty, it mostly means that there is no help.
// Use a blank space to avoid falling back on short help.
meta.LongHelp = " "
}

if g.MetaUpdate != nil {
g.MetaUpdate(&meta, field.Type.String())
}

metas = append(metas, meta)
metas = append(metas, childs...)
}

return metas
}
12 changes: 12 additions & 0 deletions gno.land/cmd/gnoland/config_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ func newConfigSetCmd(io commands.IO) *commands.Command {
},
)

// Add subcommand helpers
helperGen := metadataHelperGenerator{
MetaUpdate: func(meta *commands.Metadata, inputType string) {
meta.ShortUsage = fmt.Sprintf("config set %s <%s>", meta.Name, inputType)
},
TagNameSelector: "json",
TreeDisplay: true,
}
cmd.AddSubCommands(generateSubCommandHelper(helperGen, config.Config{}, func(_ context.Context, args []string) error {
return execConfigEdit(cfg, io, args)
})...)

return cmd
}

Expand Down
12 changes: 12 additions & 0 deletions gno.land/cmd/gnoland/secrets_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ func newSecretsGetCmd(io commands.IO) *commands.Command {
},
)

// Add subcommand helpers
helperGen := metadataHelperGenerator{
MetaUpdate: func(meta *commands.Metadata, inputType string) {
meta.ShortUsage = fmt.Sprintf("secrets get %s <%s>", meta.Name, inputType)
},
TagNameSelector: "json",
TreeDisplay: false,
}
cmd.AddSubCommands(generateSubCommandHelper(helperGen, secrets{}, func(_ context.Context, args []string) error {
return execSecretsGet(cfg, args, io)
})...)

return cmd
}

Expand Down

0 comments on commit d1a7c4d

Please sign in to comment.