mirror of
https://github.com/gogf/gf.git
synced 2025-04-05 11:18:50 +08:00
command object feature for package gcmd
This commit is contained in:
parent
5415d6dc52
commit
0622b517c5
@ -281,7 +281,7 @@ type parseWithTagInFieldStructOutput struct {
|
||||
Order string
|
||||
}
|
||||
|
||||
func (m *Model) parseWithTagInFieldStruct(field *structs.Field) (output parseWithTagInFieldStructOutput) {
|
||||
func (m *Model) parseWithTagInFieldStruct(field structs.Field) (output parseWithTagInFieldStructOutput) {
|
||||
var (
|
||||
match []string
|
||||
ormTag = field.Tag(OrmTagForStruct)
|
||||
|
@ -29,7 +29,6 @@ func Init(args ...string) {
|
||||
|
||||
// GetOpt returns the option value named `name` as gvar.Var.
|
||||
func GetOpt(name string, def ...string) *gvar.Var {
|
||||
Init()
|
||||
if v := command.GetOpt(name, def...); v != "" {
|
||||
return gvar.New(v)
|
||||
}
|
||||
@ -38,19 +37,16 @@ func GetOpt(name string, def ...string) *gvar.Var {
|
||||
|
||||
// GetOptAll returns all parsed options.
|
||||
func GetOptAll() map[string]string {
|
||||
Init()
|
||||
return command.GetOptAll()
|
||||
}
|
||||
|
||||
// ContainsOpt checks whether option named `name` exist in the arguments.
|
||||
func ContainsOpt(name string) bool {
|
||||
Init()
|
||||
return command.ContainsOpt(name)
|
||||
}
|
||||
|
||||
// GetArg returns the argument at `index` as gvar.Var.
|
||||
func GetArg(index int, def ...string) *gvar.Var {
|
||||
Init()
|
||||
if v := command.GetArg(index, def...); v != "" {
|
||||
return gvar.New(v)
|
||||
}
|
||||
@ -59,7 +55,6 @@ func GetArg(index int, def ...string) *gvar.Var {
|
||||
|
||||
// GetArgAll returns all parsed arguments.
|
||||
func GetArgAll() []string {
|
||||
Init()
|
||||
return command.GetArgAll()
|
||||
}
|
||||
|
||||
|
@ -16,22 +16,26 @@ import (
|
||||
|
||||
// Command holds the info about an argument that can handle custom logic.
|
||||
type Command struct {
|
||||
Name string // Command name(case-sensitive).
|
||||
Usage string // A brief line description about its usage, eg: gf build main.go [OPTION]
|
||||
Brief string // A brief info that describes what this command will do.
|
||||
Description string // A detailed description.
|
||||
Options []Option // Option array, configuring how this command act.
|
||||
Func Function // Custom function.
|
||||
HelpFunc Function // Custom help function
|
||||
Examples string // Usage examples.
|
||||
Additional string // Additional custom info about this command.
|
||||
parent *Command // Parent command for internal usage.
|
||||
commands []Command // Sub commands of this command.
|
||||
Name string // Command name(case-sensitive).
|
||||
Usage string // A brief line description about its usage, eg: gf build main.go [OPTION]
|
||||
Brief string // A brief info that describes what this command will do.
|
||||
Description string // A detailed description.
|
||||
Options []Option // Option array, configuring how this command act.
|
||||
Func Function // Custom function.
|
||||
FuncWithValue FuncWithValue // Custom function with output parameters that can interact with command caller.
|
||||
HelpFunc Function // Custom help function
|
||||
Examples string // Usage examples.
|
||||
Additional string // Additional custom info about this command.
|
||||
parent *Command // Parent command for internal usage.
|
||||
commands []Command // Sub commands of this command.
|
||||
}
|
||||
|
||||
// Function is a custom command callback function that is bound to a certain argument.
|
||||
type Function func(ctx context.Context, parser *Parser) (err error)
|
||||
|
||||
// FuncWithValue is similar like Func but with output parameters that can interact with command caller.
|
||||
type FuncWithValue func(ctx context.Context, parser *Parser) (out interface{}, 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 {
|
||||
@ -58,7 +62,7 @@ func (c *Command) AddCommand(commands ...Command) error {
|
||||
if cmd.Name == "" {
|
||||
return gerror.New("command name should not be empty")
|
||||
}
|
||||
if cmd.Func == nil {
|
||||
if cmd.Func == nil && cmd.FuncWithValue == nil {
|
||||
return gerror.New("command function should not be empty")
|
||||
}
|
||||
cmd.parent = c
|
||||
@ -66,3 +70,18 @@ func (c *Command) AddCommand(commands ...Command) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddObject adds one or more sub-commands to current command using struct object.
|
||||
func (c *Command) AddObject(objects ...interface{}) error {
|
||||
var (
|
||||
commands []Command
|
||||
)
|
||||
for _, object := range objects {
|
||||
tempCommand, err := NewFromObject(object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commands = append(commands, *tempCommand)
|
||||
}
|
||||
return c.AddCommand(commands...)
|
||||
}
|
||||
|
@ -8,8 +8,10 @@
|
||||
package gcmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/structs"
|
||||
@ -18,14 +20,22 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gmeta"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
"github.com/gogf/gf/v2/util/gvalid"
|
||||
)
|
||||
|
||||
const (
|
||||
tagNameDc = `dc`
|
||||
tagNameAd = `ad`
|
||||
tagNameDc = `dc`
|
||||
tagNameAd = `ad`
|
||||
tagNameRoot = `root`
|
||||
)
|
||||
|
||||
func CommandsFromObject(object interface{}) (commands []Command, err error) {
|
||||
var (
|
||||
// defaultValueTags is the struct tag names for default value storing.
|
||||
defaultValueTags = []string{"d", "default"}
|
||||
)
|
||||
|
||||
// NewFromObject creates and returns a root command object using given object.
|
||||
func NewFromObject(object interface{}) (rootCmd *Command, err error) {
|
||||
originValueAndKind := utils.OriginValueAndKind(object)
|
||||
if originValueAndKind.OriginKind != reflect.Struct {
|
||||
return nil, gerror.Newf(
|
||||
@ -33,18 +43,52 @@ func CommandsFromObject(object interface{}) (commands []Command, err error) {
|
||||
originValueAndKind.InputValue.Type().String(),
|
||||
)
|
||||
}
|
||||
//for i := 0; i < originValueAndKind.InputValue.NumMethod(); i++ {
|
||||
// method := originValueAndKind.InputValue.Method(i)
|
||||
//}
|
||||
//for _, field := range fields {
|
||||
//
|
||||
//}
|
||||
var (
|
||||
nameSet = gset.NewStrSet()
|
||||
subCommands []Command
|
||||
)
|
||||
for i := 0; i < originValueAndKind.InputValue.NumMethod(); i++ {
|
||||
var (
|
||||
root bool
|
||||
method = originValueAndKind.InputValue.Method(i)
|
||||
methodCommand Command
|
||||
)
|
||||
methodCommand, root, err = newCommandFromMethod(object, method)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if nameSet.Contains(methodCommand.Name) {
|
||||
return nil, gerror.Newf(
|
||||
`command name should be unique, found duplicated command name in method "%s"`,
|
||||
method.Type().String(),
|
||||
)
|
||||
}
|
||||
if root {
|
||||
if rootCmd != nil {
|
||||
return nil, gerror.Newf(
|
||||
`there should be only one root command in object, found duplicated in method "%s"`,
|
||||
method.Type().String(),
|
||||
)
|
||||
}
|
||||
rootCmd = &methodCommand
|
||||
} else {
|
||||
subCommands = append(subCommands, methodCommand)
|
||||
}
|
||||
}
|
||||
if rootCmd == nil {
|
||||
return nil, gerror.Newf(
|
||||
`there should be one root command in object when creating command from object, but found none in object "%s"`,
|
||||
originValueAndKind.InputValue.Type().String(),
|
||||
)
|
||||
}
|
||||
if len(subCommands) > 0 {
|
||||
err = rootCmd.AddCommand(subCommands...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newCommandFromMethod(object, method reflect.Value) (*Command, error) {
|
||||
func newCommandFromMethod(object interface{}, method reflect.Value) (command Command, root bool, err error) {
|
||||
var (
|
||||
err error
|
||||
reflectType = method.Type()
|
||||
)
|
||||
// Necessary validation for input/output parameters and naming.
|
||||
@ -52,33 +96,33 @@ func newCommandFromMethod(object, method reflect.Value) (*Command, error) {
|
||||
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(),
|
||||
`invalid command: %s.%s.%s defined as "%s", but "func(context.Context, Input)(Output, error)" is required`,
|
||||
reflectType.PkgPath(), reflect.TypeOf(object).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`,
|
||||
`invalid command: defined as "%s", but "func(context.Context, Input)(Output, error)" is required`,
|
||||
reflectType.String(),
|
||||
)
|
||||
}
|
||||
return nil, err
|
||||
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"`,
|
||||
`invalid command: defined as "%s", but the first input parameter should be type of "context.Context"`,
|
||||
reflectType.String(),
|
||||
)
|
||||
return nil, err
|
||||
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"`,
|
||||
`invalid command: defined as "%s", but the last output parameter should be type of "error"`,
|
||||
reflectType.String(),
|
||||
)
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
// The input struct should be named as `xxxInput`.
|
||||
if !gstr.HasSuffix(reflectType.In(1).String(), `Input`) {
|
||||
@ -87,7 +131,7 @@ func newCommandFromMethod(object, method reflect.Value) (*Command, error) {
|
||||
`invalid struct naming for input: defined as "%s", but it should be named with "Input" suffix like "xxxInput"`,
|
||||
reflectType.In(1).String(),
|
||||
)
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
// The output struct should be named as `xxxOutput`.
|
||||
if !gstr.HasSuffix(reflectType.Out(0).String(), `Output`) {
|
||||
@ -96,12 +140,11 @@ func newCommandFromMethod(object, method reflect.Value) (*Command, error) {
|
||||
`invalid struct naming for output: defined as "%s", but it should be named with "Output" suffix like "xxxOutput"`,
|
||||
reflectType.Out(0).String(),
|
||||
)
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
inputObject reflect.Value
|
||||
outputObject reflect.Value
|
||||
inputObject reflect.Value
|
||||
)
|
||||
if method.Type().In(1).Kind() == reflect.Ptr {
|
||||
inputObject = reflect.New(method.Type().In(1).Elem()).Elem()
|
||||
@ -109,37 +152,112 @@ func newCommandFromMethod(object, method reflect.Value) (*Command, error) {
|
||||
inputObject = reflect.New(method.Type().In(1)).Elem()
|
||||
}
|
||||
|
||||
if method.Type().Out(1).Kind() == reflect.Ptr {
|
||||
outputObject = reflect.New(method.Type().Out(0).Elem()).Elem()
|
||||
} else {
|
||||
outputObject = reflect.New(method.Type().Out(0)).Elem()
|
||||
}
|
||||
// Command creating.
|
||||
var (
|
||||
cmd = Command{}
|
||||
metaData = gmeta.Data(inputObject.Interface())
|
||||
)
|
||||
if err = gconv.Scan(metaData, &cmd); err != nil {
|
||||
return nil, err
|
||||
if err = gconv.Scan(metaData, &command); err != nil {
|
||||
return
|
||||
}
|
||||
root = gconv.Bool(metaData[tagNameRoot])
|
||||
// Name filed is necessary.
|
||||
if cmd.Name == "" {
|
||||
return nil, gerror.Newf(
|
||||
if command.Name == "" {
|
||||
err = gerror.Newf(
|
||||
`command name cannot be empty, "name" tag not found in struct "%s"`,
|
||||
inputObject.Type().String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if cmd.Description == "" {
|
||||
cmd.Description = metaData[tagNameDc]
|
||||
if command.Description == "" {
|
||||
command.Description = metaData[tagNameDc]
|
||||
}
|
||||
if cmd.Additional == "" {
|
||||
cmd.Additional = metaData[tagNameAd]
|
||||
if command.Additional == "" {
|
||||
command.Additional = metaData[tagNameAd]
|
||||
}
|
||||
|
||||
if cmd.Options, err = newOptionsFromInput(inputObject.Interface()); err != nil {
|
||||
return nil, err
|
||||
if command.Options, err = newOptionsFromInput(inputObject.Interface()); err != nil {
|
||||
return
|
||||
}
|
||||
return &cmd, nil
|
||||
|
||||
// Create function that has value return.
|
||||
command.FuncWithValue = func(ctx context.Context, parser *Parser) (out interface{}, err error) {
|
||||
defer func() {
|
||||
if exception := recover(); exception != nil {
|
||||
if v, ok := exception.(error); ok && gerror.HasStack(v) {
|
||||
err = v
|
||||
} else {
|
||||
err = gerror.New(`exception recovered:` + gconv.String(exception))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
data = gconv.Map(parser.GetOptAll())
|
||||
inputValues = []reflect.Value{reflect.ValueOf(ctx)}
|
||||
)
|
||||
if data == nil {
|
||||
data = map[string]interface{}{}
|
||||
}
|
||||
err = mergeDefaultStructValue(data, inputObject.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Construct input parameters.
|
||||
if len(data) > 0 {
|
||||
|
||||
}
|
||||
if inputObject.Kind() == reflect.Ptr {
|
||||
err = gconv.Scan(data, inputObject.Interface())
|
||||
} else {
|
||||
err = gconv.Struct(data, inputObject.Addr().Interface())
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Parameters validation.
|
||||
if err = gvalid.New().Bail().Data(inputObject.Interface()).Assoc(data).Run(ctx); err != nil {
|
||||
err = gerror.Current(err)
|
||||
return
|
||||
}
|
||||
inputValues = append(inputValues, inputObject)
|
||||
|
||||
// Call handler with dynamic created parameter values.
|
||||
results := method.Call(inputValues)
|
||||
out = results[0].Interface()
|
||||
if !results[1].IsNil() {
|
||||
if v, ok := results[1].Interface().(error); ok {
|
||||
err = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// mergeDefaultStructValue merges the request parameters with default values from struct tag definition.
|
||||
func mergeDefaultStructValue(data map[string]interface{}, pointer interface{}) error {
|
||||
tagFields, err := structs.TagFields(pointer, defaultValueTags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(tagFields) > 0 {
|
||||
var (
|
||||
foundKey string
|
||||
foundValue interface{}
|
||||
)
|
||||
for _, field := range tagFields {
|
||||
foundKey, foundValue = gutil.MapPossibleItemByKey(data, field.Name())
|
||||
if foundKey == "" {
|
||||
data[field.Name()] = field.TagValue
|
||||
} else {
|
||||
if utils.IsEmpty(foundValue) {
|
||||
data[foundKey] = field.TagValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newOptionsFromInput(object interface{}) (options []Option, err error) {
|
||||
@ -153,7 +271,7 @@ func newOptionsFromInput(object interface{}) (options []Option, err error) {
|
||||
for _, field := range fields {
|
||||
var (
|
||||
option = Option{}
|
||||
metaData = gmeta.Data(field.Value.Interface())
|
||||
metaData = field.TagMap()
|
||||
)
|
||||
if err = gconv.Scan(metaData, &option); err != nil {
|
||||
return nil, err
|
||||
|
@ -12,22 +12,29 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// Run calls custom function that bound to this command.
|
||||
func (c *Command) Run(ctx context.Context) error {
|
||||
_, err := c.RunWithValue(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// RunWithValue calls custom function that bound to this command with value output.
|
||||
func (c *Command) RunWithValue(ctx context.Context) (value interface{}, err error) {
|
||||
// Parse command arguments and options using default algorithm.
|
||||
parser, err := Parse(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
args := parser.GetArgAll()
|
||||
if len(args) == 1 {
|
||||
if c.HelpFunc != nil {
|
||||
return c.HelpFunc(ctx, parser)
|
||||
return nil, c.HelpFunc(ctx, parser)
|
||||
}
|
||||
return c.defaultHelpFunc(ctx, parser)
|
||||
return nil, c.defaultHelpFunc(ctx, parser)
|
||||
}
|
||||
|
||||
// Exclude the root binary name.
|
||||
@ -46,26 +53,32 @@ func (c *Command) Run(ctx context.Context) error {
|
||||
)
|
||||
c.Print()
|
||||
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *Command) doRun(ctx context.Context, parser *Parser) (err error) {
|
||||
func (c *Command) doRun(ctx context.Context, parser *Parser) (value interface{}, err error) {
|
||||
// Add built-in help option, just for info only.
|
||||
c.Options = append(c.Options, defaultHelpOption)
|
||||
// Check built-in help command.
|
||||
if parser.ContainsOpt(helpOptionName) || parser.ContainsOpt(helpOptionNameShort) {
|
||||
if c.HelpFunc != nil {
|
||||
return c.HelpFunc(ctx, parser)
|
||||
return nil, c.HelpFunc(ctx, parser)
|
||||
}
|
||||
return c.defaultHelpFunc(ctx, parser)
|
||||
return nil, c.defaultHelpFunc(ctx, parser)
|
||||
}
|
||||
// Reparse the arguments for current command configuration.
|
||||
parser, err = c.reParse(ctx, parser)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
// Registered command function calling.
|
||||
return c.Func(ctx, parser)
|
||||
if c.Func != nil {
|
||||
return nil, c.Func(ctx, parser)
|
||||
}
|
||||
if c.FuncWithValue != nil {
|
||||
return c.FuncWithValue(ctx, parser)
|
||||
}
|
||||
return nil, gerror.New(`no function registered for current command`)
|
||||
}
|
||||
|
||||
// reParse re-parses the arguments using option configuration of current command.
|
||||
@ -80,7 +93,7 @@ func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error)
|
||||
)
|
||||
for _, option := range c.Options {
|
||||
if option.Short != "" {
|
||||
optionKey = fmt.Sprintf(`%s.%s`, option.Name, option.Short)
|
||||
optionKey = fmt.Sprintf(`%s,%s`, option.Name, option.Short)
|
||||
} else {
|
||||
optionKey = option.Name
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ package gcmd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@ -22,21 +22,22 @@ import (
|
||||
type TestCmdObject struct{}
|
||||
|
||||
type TestCmdObjectInput struct {
|
||||
g.Meta `name:"gf" usage:"gf env/test" brief:"gf env command" dc:"description" ad:"ad"`
|
||||
g.Meta `root:"true" name:"root" usage:"root env/test" brief:"root 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" short:"n" orphan:"false" brief:"name for command"`
|
||||
g.Meta `name:"env" usage:"root env" brief:"root env command" dc:"root env command description" ad:"root env command ad"`
|
||||
}
|
||||
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" short:"n" orphan:"false" brief:"name for command"`
|
||||
g.Meta `name:"test" usage:"root test" brief:"root test command" dc:"root test command description" ad:"root test command ad"`
|
||||
Name string `v:"required" short:"n" orphan:"false" brief:"name for test command"`
|
||||
}
|
||||
type TestCmdObjectTestOutput struct {
|
||||
Content string
|
||||
}
|
||||
type TestCmdObjectTestOutput struct{}
|
||||
|
||||
func (TestCmdObject) Root(ctx context.Context, in TestCmdObjectInput) (out *TestCmdObjectOutput, err error) {
|
||||
return
|
||||
@ -47,67 +48,42 @@ func (TestCmdObject) Env(ctx context.Context, in TestCmdObjectEnvInput) (out *Te
|
||||
}
|
||||
|
||||
func (TestCmdObject) Test(ctx context.Context, in TestCmdObjectTestInput) (out *TestCmdObjectTestOutput, err error) {
|
||||
out = &TestCmdObjectTestOutput{
|
||||
Content: in.Name,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Test_Command_AddObject(t *testing.T) {
|
||||
|
||||
func Test_Command_NewFromObject(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
err error
|
||||
ctx = gctx.New()
|
||||
cmd, err = gcmd.NewFromObject(&TestCmdObject{})
|
||||
)
|
||||
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)
|
||||
}
|
||||
t.AssertNil(err)
|
||||
t.Assert(cmd.Name, "root")
|
||||
|
||||
if err = commandRoot.Run(ctx); err != nil {
|
||||
g.Log().Fatal(ctx, err)
|
||||
}
|
||||
os.Args = []string{"root", "test", "-n=john"}
|
||||
value, err := cmd.RunWithValue(ctx)
|
||||
t.AssertNil(err)
|
||||
t.Assert(value, `{"Content":"john"}`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Command_AddObject(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
command = gcmd.Command{
|
||||
Name: "start",
|
||||
}
|
||||
)
|
||||
err := command.AddObject(&TestCmdObject{})
|
||||
t.AssertNil(err)
|
||||
|
||||
os.Args = []string{"start", "root", "test", "-n=john"}
|
||||
value, err := command.RunWithValue(ctx)
|
||||
t.AssertNil(err)
|
||||
t.Assert(value, `{"Content":"john"}`)
|
||||
})
|
||||
}
|
||||
|
@ -10,25 +10,33 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/command"
|
||||
"github.com/gogf/gf/v2/internal/intlog"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfsnotify"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultCacheExpire = time.Minute // defaultCacheExpire is the expire time for file content caching in seconds.
|
||||
defaultCacheExpire = "1m" // defaultCacheExpire is the expire time for file content caching in seconds.
|
||||
commandEnvKeyForCache = "gf.gfile.cache" // commandEnvKeyForCache is the configuration key for command argument or environment configuring cache expire duration.
|
||||
)
|
||||
|
||||
var (
|
||||
// Default expire time for file content caching.
|
||||
cacheExpire = gcmd.GetOptWithEnv(commandEnvKeyForCache, defaultCacheExpire).Duration()
|
||||
cacheExpire = getCacheExpire()
|
||||
|
||||
// internalCache is the memory cache for internal usage.
|
||||
internalCache = gcache.New()
|
||||
)
|
||||
|
||||
func getCacheExpire() time.Duration {
|
||||
d, err := time.ParseDuration(command.GetOptWithEnv(commandEnvKeyForCache, defaultCacheExpire))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// GetContentsWithCache returns string content of given file by `path` from cache.
|
||||
// If there's no content in the cache, it will read it from disk file specified by `path`.
|
||||
// The parameter `expire` specifies the caching time for this file content in seconds.
|
||||
|
@ -20,11 +20,12 @@ package gtimer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/internal/command"
|
||||
)
|
||||
|
||||
// Timer is the timer manager, which uses ticks to calculate the timing interval.
|
||||
@ -47,15 +48,23 @@ const (
|
||||
StatusStopped = 2 // Job or Timer is stopped.
|
||||
StatusClosed = -1 // Job or Timer is closed and waiting to be deleted.
|
||||
panicExit = "exit" // panicExit is used for custom job exit with panic.
|
||||
defaultTimerInterval = 100 // defaultTimerInterval is the default timer interval in milliseconds.
|
||||
defaultTimerInterval = "100" // defaultTimerInterval is the default timer interval in milliseconds.
|
||||
commandEnvKeyForInterval = "gf.gtimer.interval" // commandEnvKeyForInterval is the key for command argument or environment configuring default interval duration for timer.
|
||||
)
|
||||
|
||||
var (
|
||||
defaultInterval = getDefaultInterval()
|
||||
defaultTimer = New()
|
||||
defaultInterval = gcmd.GetOptWithEnv(commandEnvKeyForInterval, defaultTimerInterval).Duration() * time.Millisecond
|
||||
)
|
||||
|
||||
func getDefaultInterval() time.Duration {
|
||||
n, err := strconv.Atoi(command.GetOptWithEnv(commandEnvKeyForInterval, defaultTimerInterval))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return time.Duration(n) * time.Millisecond
|
||||
}
|
||||
|
||||
// DefaultOptions creates and returns a default options object for Timer creation.
|
||||
func DefaultOptions() TimerOptions {
|
||||
return TimerOptions{
|
||||
|
@ -151,7 +151,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
|
||||
}
|
||||
|
||||
// Normal unmarshalling interfaces checks.
|
||||
if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok {
|
||||
if err, ok = bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
|
||||
// return v.UnmarshalValue(params)
|
||||
// }
|
||||
// Note that it's `pointerElemReflectValue` here not `pointerReflectValue`.
|
||||
if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok {
|
||||
if err, ok = bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok {
|
||||
return err
|
||||
}
|
||||
// Retrieve its element, may be struct at last.
|
||||
@ -177,7 +177,11 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
|
||||
// DO NOT use MapDeep here.
|
||||
paramsMap := Map(paramsInterface)
|
||||
if paramsMap == nil {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, "convert params to map failed: %v", params)
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`convert params "%#v" to map failed`,
|
||||
params,
|
||||
)
|
||||
}
|
||||
|
||||
// It only performs one converting to the same attribute.
|
||||
|
@ -142,10 +142,14 @@ func doDump(value interface{}, indent string, buffer *bytes.Buffer, option doDum
|
||||
doDumpNumber(exportInternalInput)
|
||||
|
||||
case reflect.Chan:
|
||||
buffer.WriteString(`<chan>`)
|
||||
buffer.WriteString(fmt.Sprintf(`<%s>`, reflect.TypeOf(value).String()))
|
||||
|
||||
case reflect.Func:
|
||||
buffer.WriteString(`<func>`)
|
||||
if reflectValue.IsNil() || !reflectValue.IsValid() {
|
||||
buffer.WriteString(`<nil>`)
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf(`<%s>`, reflect.TypeOf(value).String()))
|
||||
}
|
||||
|
||||
default:
|
||||
doDumpDefault(exportInternalInput)
|
||||
|
Loading…
x
Reference in New Issue
Block a user