Skip to content

Commit

Permalink
feat: support running commands via non-command line arguments
Browse files Browse the repository at this point in the history
Signed-off-by: oninowang <oninowang@tencent.com>
  • Loading branch information
oninowang committed Jan 30, 2024
1 parent 4fb7396 commit ba13464
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 20 deletions.
47 changes: 27 additions & 20 deletions os/gcmd/gcmd_command_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ import (
"github.com/gogf/gf/v2/util/gutil"
)

// Run calls custom function that bound to this command.
// Run calls custom function in os.Args that bound to this command.
// It exits this process with exit code 1 if any error occurs.
func (c *Command) Run(ctx context.Context) {
_ = c.RunWithValue(ctx)
}

// RunWithValue calls custom function that bound to this command with value output.
// RunWithValue calls custom function in os.Args that bound to this command with value output.
// It exits this process with exit code 1 if any error occurs.
func (c *Command) RunWithValue(ctx context.Context) (value interface{}) {
value, err := c.RunWithValueError(ctx)
Expand Down Expand Up @@ -64,45 +64,52 @@ func (c *Command) RunWithValue(ctx context.Context) (value interface{}) {
return value
}

// RunWithError calls custom function that bound to this command with error output.
// RunWithError calls custom function in os.Args that bound to this command with error output.
func (c *Command) RunWithError(ctx context.Context) (err error) {
_, err = c.RunWithValueError(ctx)
return
}

// RunWithValueError calls custom function that bound to this command with value and error output.
// RunWithValueError calls custom function in os.Args that bound to this command with value and error output.
func (c *Command) RunWithValueError(ctx context.Context) (value interface{}, err error) {
// Parse command arguments and options using default algorithm.
parser, err := Parse(nil)
return c.RunWithSpecificArgs(ctx, os.Args)
}

// RunWithSpecificArgs calls custom function in specific args that bound to this command with value and error output.
func (c *Command) RunWithSpecificArgs(ctx context.Context, args []string) (value interface{}, err error) {
if len(args) == 0 {
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "args can not be empty!")
}
parser, err := ParseArgs(args, nil)
if err != nil {
return nil, err
}
args := parser.GetArgAll()
if len(args) == 1 {
return c.doRun(ctx, parser)
parsedArgs := parser.GetArgAll()
if len(parsedArgs) == 1 {
return c.doRun(ctx, args, parser)
}

// Exclude the root binary name.
args = args[1:]
parsedArgs = parsedArgs[1:]

// Find the matched command and run it.
lastCmd, foundCmd, newCtx := c.searchCommand(ctx, args)
lastCmd, foundCmd, newCtx := c.searchCommand(ctx, parsedArgs)
if foundCmd != nil {
return foundCmd.doRun(newCtx, parser)
return foundCmd.doRun(newCtx, args, parser)
}

// Print error and help command if no command found.
err = gerror.NewCodef(
gcode.WithCode(gcode.CodeNotFound, lastCmd),
`command "%s" not found for command "%s", command line: %s`,
gstr.Join(args, " "),
gstr.Join(parsedArgs, " "),
c.Name,
gstr.Join(os.Args, " "),
gstr.Join(args, " "),
)
return
}

func (c *Command) doRun(ctx context.Context, parser *Parser) (value interface{}, err error) {
func (c *Command) doRun(ctx context.Context, args []string, parser *Parser) (value interface{}, err error) {
defer func() {
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
Expand Down Expand Up @@ -139,8 +146,8 @@ func (c *Command) doRun(ctx context.Context, parser *Parser) (value interface{},
)
defer span.End()
span.SetAttributes(gtrace.CommonLabels()...)
// Reparse the arguments for current command configuration.
parser, err = c.reParse(ctx, parser)
// Reparse the original arguments for current command configuration.
parser, err = c.reParse(ctx, args, parser)
if err != nil {
return nil, err
}
Expand All @@ -158,8 +165,8 @@ func (c *Command) doRun(ctx context.Context, parser *Parser) (value interface{},
return nil, c.defaultHelpFunc(ctx, parser)
}

// reParse parses the arguments using option configuration of current command.
func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error) {
// reParse parses the original arguments using option configuration of current command.
func (c *Command) reParse(ctx context.Context, args []string, parser *Parser) (*Parser, error) {
if len(c.Arguments) == 0 {
return parser, nil
}
Expand All @@ -178,7 +185,7 @@ func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error)
}
supportedOptions[optionKey] = !arg.Orphan
}
parser, err := Parse(supportedOptions, ParserOption{
parser, err := ParseArgs(args, supportedOptions, ParserOption{
CaseSensitive: c.CaseSensitive,
Strict: c.Strict,
})
Expand Down
40 changes: 40 additions & 0 deletions os/gcmd/gcmd_z_unit_feature_object1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,46 @@ func Test_Command_NewFromObject_RunWithValue(t *testing.T) {
})
}

func Test_Command_NewFromObject_RunWithSpecificArgs(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
ctx = gctx.New()
cmd, err = gcmd.NewFromObject(&TestCmdObject{})
)
t.AssertNil(err)
t.Assert(cmd.Name, "root")

// test short name
args := []string{"root", "test", "-n=john"}
value, err := cmd.RunWithSpecificArgs(ctx, args)
t.AssertNil(err)
t.Assert(value, `{"Name":"john","Version":false}`)

// test name tag name
args = []string{"root", "test", "-yourname=hailaz"}
value1, err1 := cmd.RunWithSpecificArgs(ctx, args)
t.AssertNil(err1)
t.Assert(value1, `{"Name":"hailaz","Version":false}`)

// test default tag value
args = []string{"root", "test"}
value2, err2 := cmd.RunWithSpecificArgs(ctx, args)
t.AssertNil(err2)
t.Assert(value2, `{"Name":"tom","Version":false}`)

// test name tag and orphan tag true
args = []string{"root", "test", "-v"}
value3, err3 := cmd.RunWithSpecificArgs(ctx, args)
t.AssertNil(err3)
t.Assert(value3, `{"Name":"tom","Version":true}`)

// test empty args
value4, err4 := cmd.RunWithSpecificArgs(ctx, nil)
t.Assert(err4, "args can not be empty!")
t.Assert(value4, nil)
})
}

func Test_Command_AddObject(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
Expand Down

0 comments on commit ba13464

Please sign in to comment.