gcmd manager feature

This commit is contained in:
John Guo
2021-11-22 11:23:46 +08:00
parent f5920fff68
commit dc1ad3dda4
5 changed files with 255 additions and 23 deletions

View File

@ -35,24 +35,24 @@ type Function func(ctx context.Context, parser *Parser) (err error)
// Option is the command value that is specified by a name or shor name.
// An Option can have or have no value bound to it.
type Option struct {
Name string // Option name.
Short string // Option short.
Brief string // Brief info about this Option, which is used in help info.
NeedValue bool // Whether this Option having or having no value bound to it.
Name string // Option name.
Short string // Option short.
Brief string // Brief info about this Option, which is used in help info.
Orphan bool // Whether this Option having or having no value bound to it.
}
var (
// defaultHelpOption is the default help option that will be automatically added to each command.
defaultHelpOption = Option{
Name: `help`,
Short: `h`,
Brief: `more information about this command`,
NeedValue: false,
Name: `help`,
Short: `h`,
Brief: `more information about this command`,
Orphan: true,
}
)
// Add adds one or more sub-commands to current command.
func (c *Command) Add(commands ...Command) error {
// 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 == "" {

View File

@ -0,0 +1,119 @@
// 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.
//
package gcmd
import (
"reflect"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gmeta"
)
const (
tagNameName = `name`
tagNameUsage = `usage`
tagNameBrief = `brief`
tagNameShort = `short`
tagNameOrphan = `orphan`
tagNameDescription = `description`
tagNameDc = `dc`
tagNameAddition = `additional`
tagNameAd = `ad`
)
func (c *Command) AddObject(objects ...interface{}) (err error) {
for _, object := range objects {
if err = c.doAddObject(object); err != nil {
return err
}
}
return nil
}
func (c *Command) doAddObject(object interface{}) error {
originValueAndKind := utils.OriginValueAndKind(object)
if originValueAndKind.OriginKind != reflect.Struct {
return gerror.Newf(
`input object should be type of struct, but got "%s"`,
originValueAndKind.InputValue.Type().String(),
)
}
for i := 0; i < originValueAndKind.InputValue.NumMethod(); i++ {
method := originValueAndKind.InputValue.Method(i)
}
for _, field := range fields {
}
return nil
}
func newCommandFromMethod(object, method reflect.Value) (cmd *Command, err error) {
var (
reflectType = method.Type()
)
if reflectType.NumIn() != 2 || reflectType.NumOut() != 2 {
if reflectType.PkgPath() != "" {
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`invalid handler: %s.%s.%s defined as "%s", but "func(context.Context, Input)(Output, error)" is required`,
reflectType.PkgPath(), object.Type().Name(), reflectType.Name(), reflectType.String(),
)
} else {
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`invalid handler: defined as "%s", but "func(context.Context, Input)(Output, error)" is required`,
reflectType.String(),
)
}
return
}
if reflectType.In(0).String() != "context.Context" {
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`invalid handler: defined as "%s", but the first input parameter should be type of "context.Context"`,
reflectType.String(),
)
return
}
if reflectType.Out(1).String() != "error" {
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`invalid handler: defined as "%s", but the last output parameter should be type of "error"`,
reflectType.String(),
)
return
}
// The input struct should be named as `xxxInput`.
if !gstr.HasSuffix(reflectType.In(1).String(), `Input`) {
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`invalid struct naming for input: defined as "%s", but it should be named with "Input" suffix like "xxxInput"`,
reflectType.In(1).String(),
)
return
}
// The output struct should be named as `xxxOutput`.
if !gstr.HasSuffix(reflectType.Out(0).String(), `Output`) {
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`invalid struct naming for output: defined as "%s", but it should be named with "Output" suffix like "xxxOutput"`,
reflectType.Out(0).String(),
)
return
}
var (
metaMap = gmeta.Data()
)
}

View File

@ -84,7 +84,7 @@ func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error)
} else {
optionKey = option.Name
}
supportedOptions[optionKey] = option.NeedValue
supportedOptions[optionKey] = !option.Orphan
}
return Parse(supportedOptions)
}

View File

@ -0,0 +1,113 @@
// 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 gcmd_test
import (
"context"
"fmt"
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/test/gtest"
)
type TestCmdObject struct{}
type TestCmdObjectInput struct {
g.Meta `name:"gf" usage:"gf env/test" brief:"gf env command" dc:"description" ad:"ad"`
}
type TestCmdObjectOutput struct{}
type TestCmdObjectEnvInput struct {
g.Meta `name:"env" usage:"gf env/test" brief:"gf env command" dc:"description" ad:"ad"`
Name string `v:"required" name:"name" short:"n" orphan:"false" brief:"name for command"`
}
type TestCmdObjectEnvOutput struct{}
type TestCmdObjectTestInput struct {
g.Meta `name:"test" usage:"gf env/test" brief:"gf test command" dc:"description" ad:"ad"`
Name string `v:"required" name:"name" short:"n" orphan:"false" brief:"name for command"`
}
type TestCmdObjectTestOutput struct{}
func (TestCmdObject) Root(ctx context.Context, in TestCmdObjectInput) (out *TestCmdObjectOutput, err error) {
return
}
func (TestCmdObject) Env(ctx context.Context, in TestCmdObjectEnvInput) (out *TestCmdObjectEnvOutput, err error) {
return
}
func (TestCmdObject) Test(ctx context.Context, in TestCmdObjectTestInput) (out *TestCmdObjectTestOutput, err error) {
return
}
func Test_Command_AddObject(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
ctx = gctx.New()
err error
)
commandRoot := &gcmd.Command{
Name: "gf",
}
// env
commandEnv := gcmd.Command{
Name: "env",
Func: func(ctx context.Context, parser *gcmd.Parser) error {
fmt.Println("env")
return nil
},
}
// test
commandTest := gcmd.Command{
Name: "test",
Brief: "test brief",
Description: "test description current Golang environment variables",
Examples: `
gf get github.com/gogf/gf
gf get github.com/gogf/gf@latest
gf get github.com/gogf/gf@master
gf get golang.org/x/sys
`,
Options: []gcmd.Option{
{
Name: "my-option",
Short: "o",
Brief: "It's my custom option",
Orphan: false,
},
{
Name: "another",
Short: "a",
Brief: "It's my another custom option",
Orphan: false,
},
},
Func: func(ctx context.Context, parser *gcmd.Parser) error {
fmt.Println("test")
return nil
},
}
err = commandRoot.AddCommand(
commandEnv,
commandTest,
)
if err != nil {
g.Log().Fatal(ctx, err)
}
if err = commandRoot.Run(ctx); err != nil {
g.Log().Fatal(ctx, err)
}
})
}

View File

@ -101,16 +101,16 @@ gf get golang.org/x/sys
`,
Options: []gcmd.Option{
{
Name: "my-option",
Short: "o",
Brief: "It's my custom option",
NeedValue: false,
Name: "my-option",
Short: "o",
Brief: "It's my custom option",
Orphan: true,
},
{
Name: "another",
Short: "a",
Brief: "It's my another custom option",
NeedValue: false,
Name: "another",
Short: "a",
Brief: "It's my another custom option",
Orphan: true,
},
},
Func: func(ctx context.Context, parser *gcmd.Parser) error {
@ -118,7 +118,7 @@ gf get golang.org/x/sys
return nil
},
}
err = commandRoot.Add(
err = commandRoot.AddCommand(
commandEnv,
commandTest,
)
@ -153,7 +153,7 @@ Use 'gf help COMMAND' or 'gf COMMAND -h' for detail about a command, which has '
return nil
},
}
if err = c.Add(commandEnv); err != nil {
if err = c.AddCommand(commandEnv); err != nil {
g.Log().Fatal(ctx, err)
}
// get
@ -172,7 +172,7 @@ gf get golang.org/x/sys
return nil
},
}
if err = c.Add(commandGet); err != nil {
if err = c.AddCommand(commandGet); err != nil {
g.Log().Fatal(ctx, err)
}
// build
@ -212,7 +212,7 @@ gf build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./dock
return nil
},
}
if err = c.Add(commandBuild); err != nil {
if err = c.AddCommand(commandBuild); err != nil {
g.Log().Fatal(ctx, err)
}
c.Run(ctx)