Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: #3390 name&shor tag mapping failed to command input object for package gcmd #3429

Merged
merged 6 commits into from Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions internal/command/command.go
Expand Up @@ -17,7 +17,7 @@ import (
var (
defaultParsedArgs = make([]string, 0)
defaultParsedOptions = make(map[string]string)
argumentRegex = regexp.MustCompile(`^\-{1,2}([\w\?\.\-]+)(=){0,1}(.*)$`)
argumentOptionRegex = regexp.MustCompile(`^\-{1,2}([\w\?\.\-]+)(=){0,1}(.*)$`)
)

// Init does custom initialization.
Expand All @@ -41,22 +41,22 @@ func ParseUsingDefaultAlgorithm(args ...string) (parsedArgs []string, parsedOpti
parsedArgs = make([]string, 0)
parsedOptions = make(map[string]string)
for i := 0; i < len(args); {
array := argumentRegex.FindStringSubmatch(args[i])
array := argumentOptionRegex.FindStringSubmatch(args[i])
if len(array) > 2 {
if array[2] == "=" {
parsedOptions[array[1]] = array[3]
} else if i < len(args)-1 {
if len(args[i+1]) > 0 && args[i+1][0] == '-' {
// Eg: gf gen -d -n 1
// Example: gf gen -d -n 1
parsedOptions[array[1]] = array[3]
} else {
// Eg: gf gen -n 2
// Example: gf gen -n 2
parsedOptions[array[1]] = args[i+1]
i += 2
continue
}
} else {
// Eg: gf gen -h
// Example: gf gen -h
parsedOptions[array[1]] = array[3]
}
} else {
Expand Down
6 changes: 3 additions & 3 deletions os/gcmd/gcmd.go
Expand Up @@ -18,9 +18,9 @@ import (
)

const (
CtxKeyParser gctx.StrKey = `CtxKeyParser`
CtxKeyCommand gctx.StrKey = `CtxKeyCommand`
CtxKeyArguments gctx.StrKey = `CtxKeyArguments`
CtxKeyParser gctx.StrKey = `CtxKeyParser`
CtxKeyCommand gctx.StrKey = `CtxKeyCommand`
CtxKeyArgumentsIndex gctx.StrKey = `CtxKeyArgumentsIndex`
)

const (
Expand Down
21 changes: 17 additions & 4 deletions os/gcmd/gcmd_command_object.go
Expand Up @@ -251,6 +251,9 @@ func newCommandFromMethod(
return
}

// For input struct converting using priority tag.
var priorityTag = gstr.Join([]string{tagNameName, tagNameShort}, ",")

// =============================================================================================
// Create function that has value return.
// =============================================================================================
Expand All @@ -259,9 +262,16 @@ func newCommandFromMethod(
var (
data = gconv.Map(parser.GetOptAll())
argIndex = 0
arguments = gconv.Strings(ctx.Value(CtxKeyArguments))
arguments = parser.GetArgAll()
inputValues = []reflect.Value{reflect.ValueOf(ctx)}
)
if value := ctx.Value(CtxKeyArgumentsIndex); value != nil {
argIndex = value.(int)
// Use the left args to assign to input struct object.
if argIndex < len(arguments) {
arguments = arguments[argIndex:]
}
}
if data == nil {
data = map[string]interface{}{}
}
Expand All @@ -278,8 +288,11 @@ func newCommandFromMethod(
if arg.Orphan {
if orphanValue := parser.GetOpt(arg.Name); orphanValue != nil {
if orphanValue.String() == "" {
// Eg: gf -f
// Example: gf -f
data[arg.Name] = "true"
if arg.Short != "" {
data[arg.Short] = "true"
}
} else {
// Adapter with common user habits.
// Eg:
Expand All @@ -301,9 +314,9 @@ func newCommandFromMethod(
return fmt.Sprintf(`input command data map: %s`, gjson.MustEncode(data))
})
if inputObject.Kind() == reflect.Ptr {
err = gconv.Scan(data, inputObject.Interface())
err = gconv.StructTag(data, inputObject.Interface(), priorityTag)
} else {
err = gconv.Struct(data, inputObject.Addr().Interface())
err = gconv.StructTag(data, inputObject.Addr().Interface(), priorityTag)
}
intlog.PrintFunc(ctx, func() string {
return fmt.Sprintf(`input object assigned data: %s`, gjson.MustEncode(inputObject.Interface()))
Expand Down
22 changes: 12 additions & 10 deletions os/gcmd/gcmd_command_run.go
Expand Up @@ -93,7 +93,7 @@ func (c *Command) RunWithSpecificArgs(ctx context.Context, args []string) (value
parsedArgs = parsedArgs[1:]

// Find the matched command and run it.
lastCmd, foundCmd, newCtx := c.searchCommand(ctx, parsedArgs)
lastCmd, foundCmd, newCtx := c.searchCommand(ctx, parsedArgs, 0)
if foundCmd != nil {
return foundCmd.doRun(newCtx, args, parser)
}
Expand Down Expand Up @@ -215,25 +215,27 @@ func (c *Command) reParse(ctx context.Context, args []string, parser *Parser) (*
}

// searchCommand recursively searches the command according given arguments.
func (c *Command) searchCommand(ctx context.Context, args []string) (lastCmd, foundCmd *Command, newCtx context.Context) {
func (c *Command) searchCommand(
ctx context.Context, args []string, fromArgIndex int,
) (lastCmd, foundCmd *Command, newCtx context.Context) {
if len(args) == 0 {
return c, nil, ctx
}
for _, cmd := range c.commands {
// Recursively searching the command.
// String comparison case-sensitive.
if cmd.Name == args[0] {
leftArgs := args[1:]
// If this command needs argument,
// it then gives all its left arguments to it.
if cmd.hasArgumentFromIndex() {
ctx = context.WithValue(ctx, CtxKeyArguments, leftArgs)
// it then gives all its left arguments to it using arg index marks.
//
// Note that the args here (using default args parsing) could be different with the args
// that are parsed in command.
if cmd.hasArgumentFromIndex() || len(leftArgs) == 0 {
ctx = context.WithValue(ctx, CtxKeyArgumentsIndex, fromArgIndex+1)
return c, cmd, ctx
}
// Recursively searching.
if len(leftArgs) == 0 {
return c, cmd, ctx
}
return cmd.searchCommand(ctx, leftArgs)
return cmd.searchCommand(ctx, leftArgs, fromArgIndex+1)
}
}
return c, nil, ctx
Expand Down
21 changes: 17 additions & 4 deletions os/gcmd/gcmd_parser.go
Expand Up @@ -185,11 +185,24 @@ func (p *Parser) isOptionNeedArgument(name string) bool {

// setOptionValue sets the option value for name and according alias.
func (p *Parser) setOptionValue(name, value string) {
// Accurate option name match.
for optionName := range p.passedOptions {
array := gstr.SplitAndTrim(optionName, ",")
for _, v := range array {
if strings.EqualFold(v, name) {
for _, v := range array {
optionNameAndShort := gstr.SplitAndTrim(optionName, ",")
for _, optionNameItem := range optionNameAndShort {
if optionNameItem == name {
for _, v := range optionNameAndShort {
p.parsedOptions[v] = value
}
return
}
}
}
// Fuzzy option name match.
for optionName := range p.passedOptions {
optionNameAndShort := gstr.SplitAndTrim(optionName, ",")
for _, optionNameItem := range optionNameAndShort {
if strings.EqualFold(optionNameItem, name) {
for _, v := range optionNameAndShort {
p.parsedOptions[v] = value
}
return
Expand Down