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

feat: support running commands via non-command line arguments #3290

Merged
merged 1 commit into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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