Merge branch 'master' of https://github.com/gogf/gf into gtree_Example

This commit is contained in:
huangqian
2021-11-23 20:57:30 +08:00
14 changed files with 508 additions and 82 deletions

View File

@ -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)

View File

@ -123,7 +123,7 @@ type FieldMapInput struct {
Pointer interface{}
// PriorityTagArray specifies the priority tag array for retrieving from high to low.
// If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name.
// If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name.
PriorityTagArray []string
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
@ -132,12 +132,12 @@ type FieldMapInput struct {
}
// Fields retrieves and returns the fields of `pointer` as slice.
func Fields(in FieldsInput) ([]*Field, error) {
func Fields(in FieldsInput) ([]Field, error) {
var (
ok bool
fieldFilterMap = make(map[string]struct{})
retrievedFields = make([]*Field, 0)
currentLevelFieldMap = make(map[string]*Field)
retrievedFields = make([]Field, 0)
currentLevelFieldMap = make(map[string]Field)
)
rangeFields, err := getFieldValues(in.Pointer)
if err != nil {
@ -187,7 +187,7 @@ func Fields(in FieldsInput) ([]*Field, error) {
continue
}
fieldFilterMap[fieldName] = struct{}{}
if v := currentLevelFieldMap[fieldName]; v == nil {
if v, ok := currentLevelFieldMap[fieldName]; !ok {
retrievedFields = append(retrievedFields, structField)
} else {
retrievedFields = append(retrievedFields, v)
@ -204,25 +204,25 @@ func Fields(in FieldsInput) ([]*Field, error) {
return retrievedFields, nil
}
// FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`.
// FieldMap retrieves and returns struct field as map[name/tag]Field from `pointer`.
//
// The parameter `pointer` should be type of struct/*struct.
//
// The parameter `priority` specifies the priority tag array for retrieving from high to low.
// If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name.
// If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name.
//
// The parameter `recursive` specifies the whether retrieving the fields recursively if the attribute
// is an embedded struct.
//
// Note that it only retrieves the exported attributes with first letter up-case from struct.
func FieldMap(in FieldMapInput) (map[string]*Field, error) {
func FieldMap(in FieldMapInput) (map[string]Field, error) {
fields, err := getFieldValues(in.Pointer)
if err != nil {
return nil, err
}
var (
tagValue = ""
mapField = make(map[string]*Field)
mapField = make(map[string]Field)
)
for _, field := range fields {
// Only retrieve exported attributes.

View File

@ -65,14 +65,14 @@ func ParseTag(tag string) map[string]string {
return data
}
// TagFields retrieves and returns struct tags as []*Field from `pointer`.
// TagFields retrieves and returns struct tags as []Field from `pointer`.
//
// The parameter `pointer` should be type of struct/*struct.
//
// Note that,
// 1. It only retrieves the exported attributes with first letter up-case from struct.
// 2. The parameter `priority` should be given, it only retrieves fields that has given tag.
func TagFields(pointer interface{}, priority []string) ([]*Field, error) {
func TagFields(pointer interface{}, priority []string) ([]Field, error) {
return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{})
}
@ -95,18 +95,18 @@ func TagMapName(pointer interface{}, priority []string) (map[string]string, erro
return tagMap, nil
}
// TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it.
// TagMapField retrieves struct tags as map[tag]Field from `pointer`, and returns it.
// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
//
// Note that,
// 1. It only retrieves the exported attributes with first letter up-case from struct.
// 2. The parameter `priority` should be given, it only retrieves fields that has given tag.
func TagMapField(object interface{}, priority []string) (map[string]*Field, error) {
func TagMapField(object interface{}, priority []string) (map[string]Field, error) {
fields, err := TagFields(object, priority)
if err != nil {
return nil, err
}
tagMap := make(map[string]*Field, len(fields))
tagMap := make(map[string]Field, len(fields))
for _, field := range fields {
tagField := field
tagMap[field.TagValue] = tagField
@ -114,7 +114,7 @@ func TagMapField(object interface{}, priority []string) (map[string]*Field, erro
return tagMap, nil
}
func getFieldValues(value interface{}) ([]*Field, error) {
func getFieldValues(value interface{}) ([]Field, error) {
var (
reflectValue reflect.Value
reflectKind reflect.Kind
@ -156,10 +156,10 @@ exitLoop:
var (
structType = reflectValue.Type()
length = reflectValue.NumField()
fields = make([]*Field, length)
fields = make([]Field, length)
)
for i := 0; i < length; i++ {
fields[i] = &Field{
fields[i] = Field{
Value: reflectValue.Field(i),
Field: structType.Field(i),
}
@ -167,14 +167,14 @@ exitLoop:
return fields, nil
}
func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap map[string]struct{}) ([]*Field, error) {
func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap map[string]struct{}) ([]Field, error) {
fields, err := getFieldValues(pointer)
if err != nil {
return nil, err
}
var (
tagValue = ""
tagFields = make([]*Field, 0)
tagFields = make([]Field, 0)
)
for _, field := range fields {
// Only retrieve exported attributes.

View File

@ -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()
}

View File

@ -16,49 +16,53 @@ 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 {
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 == "" {
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) Add(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...)
}

View File

@ -0,0 +1,285 @@
// 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 (
"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"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/text/gstr"
"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`
tagNameRoot = `root`
)
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(
`input object should be type of struct, but got "%s"`,
originValueAndKind.InputValue.Type().String(),
)
}
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 interface{}, method reflect.Value) (command Command, root bool, err error) {
var (
reflectType = method.Type()
)
// Necessary validation for input/output parameters and naming.
if reflectType.NumIn() != 2 || reflectType.NumOut() != 2 {
if reflectType.PkgPath() != "" {
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`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 command: 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 command: 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 command: 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 (
inputObject reflect.Value
)
if method.Type().In(1).Kind() == reflect.Ptr {
inputObject = reflect.New(method.Type().In(1).Elem()).Elem()
} else {
inputObject = reflect.New(method.Type().In(1)).Elem()
}
// Command creating.
var (
metaData = gmeta.Data(inputObject.Interface())
)
if err = gconv.Scan(metaData, &command); err != nil {
return
}
root = gconv.Bool(metaData[tagNameRoot])
// Name filed is necessary.
if command.Name == "" {
err = gerror.Newf(
`command name cannot be empty, "name" tag not found in struct "%s"`,
inputObject.Type().String(),
)
return
}
if command.Description == "" {
command.Description = metaData[tagNameDc]
}
if command.Additional == "" {
command.Additional = metaData[tagNameAd]
}
if command.Options, err = newOptionsFromInput(inputObject.Interface()); err != nil {
return
}
// 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) {
var (
fields []structs.Field
)
fields, err = structs.Fields(structs.FieldsInput{
Pointer: object,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
})
for _, field := range fields {
var (
option = Option{}
metaData = field.TagMap()
)
if err = gconv.Scan(metaData, &option); err != nil {
return nil, err
}
if option.Name == "" {
option.Name = field.Name()
}
options = append(options, option)
}
return
}

View File

@ -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,11 +93,11 @@ 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
}
supportedOptions[optionKey] = option.NeedValue
supportedOptions[optionKey] = !option.Orphan
}
return Parse(supportedOptions)
}

View File

@ -0,0 +1,89 @@
// 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"
"os"
"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 `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:"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:"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
}
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) {
out = &TestCmdObjectTestOutput{
Content: in.Name,
}
return
}
func Test_Command_NewFromObject(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")
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"}`)
})
}

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)

View File

@ -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.

View File

@ -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{

View File

@ -43,7 +43,7 @@ type ParameterRef struct {
Value *Parameter
}
func (oai *OpenApiV3) newParameterRefWithStructMethod(field *structs.Field, path, method string) (*ParameterRef, error) {
func (oai *OpenApiV3) newParameterRefWithStructMethod(field structs.Field, path, method string) (*ParameterRef, error) {
var (
tagMap = field.TagMap()
parameter = &Parameter{

View File

@ -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.

View File

@ -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)