From 0622b517c5310f5ef8048e2310b33f90ded275c5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 23 Nov 2021 20:26:55 +0800 Subject: [PATCH] command object feature for package gcmd --- database/gdb/gdb_model_with.go | 2 +- os/gcmd/gcmd.go | 5 - os/gcmd/gcmd_command.go | 43 +++-- os/gcmd/gcmd_command_object.go | 200 ++++++++++++++++----- os/gcmd/gcmd_command_run.go | 33 ++-- os/gcmd/gcmd_z_unit_feature_object_test.go | 100 ++++------- os/gfile/gfile_cache.go | 14 +- os/gtimer/gtimer.go | 15 +- util/gconv/gconv_struct.go | 10 +- util/gutil/gutil_dump.go | 8 +- 10 files changed, 288 insertions(+), 142 deletions(-) diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 8be613401..458c12a47 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -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) diff --git a/os/gcmd/gcmd.go b/os/gcmd/gcmd.go index 8278521a0..fcbefd8bf 100644 --- a/os/gcmd/gcmd.go +++ b/os/gcmd/gcmd.go @@ -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() } diff --git a/os/gcmd/gcmd_command.go b/os/gcmd/gcmd_command.go index 11f500fd2..90517a633 100644 --- a/os/gcmd/gcmd_command.go +++ b/os/gcmd/gcmd_command.go @@ -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...) +} diff --git a/os/gcmd/gcmd_command_object.go b/os/gcmd/gcmd_command_object.go index 1c80e3f87..40a580c83 100644 --- a/os/gcmd/gcmd_command_object.go +++ b/os/gcmd/gcmd_command_object.go @@ -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 diff --git a/os/gcmd/gcmd_command_run.go b/os/gcmd/gcmd_command_run.go index 0a989eaf8..1b1ba3452 100644 --- a/os/gcmd/gcmd_command_run.go +++ b/os/gcmd/gcmd_command_run.go @@ -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 } diff --git a/os/gcmd/gcmd_z_unit_feature_object_test.go b/os/gcmd/gcmd_z_unit_feature_object_test.go index 986f361a1..d8d220860 100644 --- a/os/gcmd/gcmd_z_unit_feature_object_test.go +++ b/os/gcmd/gcmd_z_unit_feature_object_test.go @@ -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"}`) }) } diff --git a/os/gfile/gfile_cache.go b/os/gfile/gfile_cache.go index 2ed7854d8..a2190fcb8 100644 --- a/os/gfile/gfile_cache.go +++ b/os/gfile/gfile_cache.go @@ -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. diff --git a/os/gtimer/gtimer.go b/os/gtimer/gtimer.go index f2af5133a..bbe226d5e 100644 --- a/os/gtimer/gtimer.go +++ b/os/gtimer/gtimer.go @@ -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{ diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index ad400507a..5526b34d8 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -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. diff --git a/util/gutil/gutil_dump.go b/util/gutil/gutil_dump.go index 642a01161..e5ee3f41b 100644 --- a/util/gutil/gutil_dump.go +++ b/util/gutil/gutil_dump.go @@ -142,10 +142,14 @@ func doDump(value interface{}, indent string, buffer *bytes.Buffer, option doDum doDumpNumber(exportInternalInput) case reflect.Chan: - buffer.WriteString(``) + buffer.WriteString(fmt.Sprintf(`<%s>`, reflect.TypeOf(value).String())) case reflect.Func: - buffer.WriteString(``) + if reflectValue.IsNil() || !reflectValue.IsValid() { + buffer.WriteString(``) + } else { + buffer.WriteString(fmt.Sprintf(`<%s>`, reflect.TypeOf(value).String())) + } default: doDumpDefault(exportInternalInput)