From 1015fb5338080d0d199a3a46b16c2523b5ad8c4f Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 13 Dec 2021 19:59:41 +0800 Subject: [PATCH 1/4] improve package gcmd --- os/gcmd/gcmd_command_object.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/os/gcmd/gcmd_command_object.go b/os/gcmd/gcmd_command_object.go index dabe68816..da33741f5 100644 --- a/os/gcmd/gcmd_command_object.go +++ b/os/gcmd/gcmd_command_object.go @@ -327,14 +327,14 @@ func newArgumentsFromInput(object interface{}) (args []Argument, err error) { } if arg.Name == helpOptionName { return nil, gerror.Newf( - `option name "%s" is already token by built-in options`, - arg.Name, + `argument name "%s" defined in "%s.%s" is already token by built-in arguments`, + arg.Name, reflect.TypeOf(object).String(), field.Name(), ) } if arg.Short == helpOptionNameShort { return nil, gerror.Newf( - `short option name "%s" is already token by built-in options`, - arg.Short, + `short argument name "%s" defined in "%s.%s" is already token by built-in arguments`, + arg.Short, reflect.TypeOf(object).String(), field.Name(), ) } if v, ok := metaData[tagNameArg]; ok { From 80daed6bdf6658f70314f6e7d62460565cd4bdae Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 13 Dec 2021 20:18:13 +0800 Subject: [PATCH 2/4] add repeated checks for command name, argument name and short name for package gcmd --- os/gcmd/gcmd_command.go | 30 +++++++++++++++++++++++++----- os/gcmd/gcmd_command_object.go | 23 ++++++++++++++++++++++- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/os/gcmd/gcmd_command.go b/os/gcmd/gcmd_command.go index 50ac66b98..a7d018786 100644 --- a/os/gcmd/gcmd_command.go +++ b/os/gcmd/gcmd_command.go @@ -10,6 +10,7 @@ package gcmd import ( "context" + "github.com/gogf/gf/v2/container/gset" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/text/gstr" ) @@ -70,16 +71,35 @@ func CommandFromCtx(ctx context.Context) *Command { // AddCommand adds one or more sub-commands to current command. func (c *Command) AddCommand(commands ...*Command) error { for _, cmd := range commands { - cmd.Name = gstr.Trim(cmd.Name) - if cmd.Name == "" { - return gerror.New("command name should not be empty") + if err := c.doAddCommand(cmd); err != nil { + return err } - cmd.parent = c - c.commands = append(c.commands, cmd) } return nil } +// doAddCommand adds one sub-command to current command. +func (c *Command) doAddCommand(command *Command) error { + command.Name = gstr.Trim(command.Name) + if command.Name == "" { + return gerror.New("command name should not be empty") + } + // Repeated check. + var ( + commandNameSet = gset.NewStrSet() + ) + for _, cmd := range c.commands { + commandNameSet.Add(cmd.Name) + } + if commandNameSet.Contains(command.Name) { + return gerror.Newf(`command "%s" is already added to command "%s"`, command.Name, c.Name) + } + // Add the given command to its sub-commands array. + command.parent = c + c.commands = append(c.commands, command) + return nil +} + // AddObject adds one or more sub-commands to current command using struct object. func (c *Command) AddObject(objects ...interface{}) error { var ( diff --git a/os/gcmd/gcmd_command_object.go b/os/gcmd/gcmd_command_object.go index da33741f5..9308bf8d7 100644 --- a/os/gcmd/gcmd_command_object.go +++ b/os/gcmd/gcmd_command_object.go @@ -308,7 +308,9 @@ func newCommandFromMethod(object interface{}, method reflect.Value) (command *Co func newArgumentsFromInput(object interface{}) (args []Argument, err error) { var ( - fields []gstructs.Field + fields []gstructs.Field + nameSet = gset.NewStrSet() + shortSet = gset.NewStrSet() ) fields, err = gstructs.Fields(gstructs.FieldsInput{ Pointer: object, @@ -340,8 +342,27 @@ func newArgumentsFromInput(object interface{}) (args []Argument, err error) { if v, ok := metaData[tagNameArg]; ok { arg.IsArg = gconv.Bool(v) } + if nameSet.Contains(arg.Name) { + return nil, gerror.Newf( + `argument name "%s" defined in "%s.%s" is already token by other argument`, + arg.Name, reflect.TypeOf(object).String(), field.Name(), + ) + } + nameSet.Add(arg.Name) + + if arg.Short != "" { + if shortSet.Contains(arg.Short) { + return nil, gerror.Newf( + `short argument name "%s" defined in "%s.%s" is already token by other argument`, + arg.Short, reflect.TypeOf(object).String(), field.Name(), + ) + } + shortSet.Add(arg.Short) + } + args = append(args, arg) } + return } From 7d84ab761ba6bf8a6fd4a652dfb8ac0efba3f0c9 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 13 Dec 2021 20:30:56 +0800 Subject: [PATCH 3/4] improve package gcmd for help content printing --- os/gcmd/gcmd_command_help.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/os/gcmd/gcmd_command_help.go b/os/gcmd/gcmd_command_help.go index de31ff97d..1d86f06dd 100644 --- a/os/gcmd/gcmd_command_help.go +++ b/os/gcmd/gcmd_command_help.go @@ -42,11 +42,14 @@ func (c *Command) Print() { name = p.parent.Name + " " + name p = p.parent } - if c.hasArgumentFromIndex() { - buffer.WriteString(fmt.Sprintf(`%s ARGUMENT [OPTION]`, name)) - } else { - buffer.WriteString(fmt.Sprintf(`%s [OPTION]`, name)) + buffer.WriteString(name) + if len(c.commands) > 0 { + buffer.WriteString(` COMMAND`) } + if c.hasArgumentFromIndex() { + buffer.WriteString(` ARGUMENT`) + } + buffer.WriteString(` [OPTION]`) } buffer.WriteString("\n\n") } From bb1a95fff873d75d8bc940bfd525b2682eea3a2c Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 14 Dec 2021 21:01:36 +0800 Subject: [PATCH 4/4] fix issue in gproc.ShellExec --- os/gproc/gproc.go | 6 ++++-- os/gproc/gproc_z_unit_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 os/gproc/gproc_z_unit_test.go diff --git a/os/gproc/gproc.go b/os/gproc/gproc.go index ca2f777ab..3c9607383 100644 --- a/os/gproc/gproc.go +++ b/os/gproc/gproc.go @@ -103,7 +103,7 @@ func ShellRun(cmd string) error { } // ShellExec executes given command `cmd` synchronously and returns the command result. -func ShellExec(cmd string, environment ...[]string) (string, error) { +func ShellExec(cmd string, environment ...[]string) (result string, err error) { var ( buf = bytes.NewBuffer(nil) p = NewProcess( @@ -114,7 +114,9 @@ func ShellExec(cmd string, environment ...[]string) (string, error) { ) p.Stdout = buf p.Stderr = buf - return buf.String(), p.Run() + err = p.Run() + result = buf.String() + return } // parseCommand parses command `cmd` into slice arguments. diff --git a/os/gproc/gproc_z_unit_test.go b/os/gproc/gproc_z_unit_test.go new file mode 100644 index 000000000..a02087d3b --- /dev/null +++ b/os/gproc/gproc_z_unit_test.go @@ -0,0 +1,29 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" -benchmem + +package gproc_test + +import ( + "testing" + + "github.com/gogf/gf/v2/os/gproc" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_ShellExec(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + s, err := gproc.ShellExec(`echo 123`) + t.AssertNil(err) + t.Assert(s, "123\n") + }) + // error + gtest.C(t, func(t *gtest.T) { + _, err := gproc.ShellExec(`NoneExistCommandCall`) + t.AssertNE(err, nil) + }) +}