diff --git a/os/gcmd/gcmd_command.go b/os/gcmd/gcmd_command.go index a7d018786..347b8e66d 100644 --- a/os/gcmd/gcmd_command.go +++ b/os/gcmd/gcmd_command.go @@ -28,6 +28,7 @@ type Command struct { Examples string // Usage examples. Additional string // Additional info about this command, which will be appended to the end of help info. Strict bool // Strict parsing options, which means it returns error if invalid option given. + CaseSensitive bool // CaseSensitive parsing options, which means it parses input options in case-sensitive way. Config string // Config node name, which also retrieves the values from config component along with command line. parent *Command // Parent command for internal usage. commands []*Command // Sub commands of this command. diff --git a/os/gcmd/gcmd_command_object.go b/os/gcmd/gcmd_command_object.go index ddf37151d..2dbeb2489 100644 --- a/os/gcmd/gcmd_command_object.go +++ b/os/gcmd/gcmd_command_object.go @@ -9,11 +9,14 @@ package gcmd import ( "context" + "fmt" "reflect" "github.com/gogf/gf/v2/container/gset" + "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/internal/intlog" "github.com/gogf/gf/v2/internal/reflection" "github.com/gogf/gf/v2/internal/utils" "github.com/gogf/gf/v2/os/gstructs" @@ -285,11 +288,17 @@ func newCommandFromMethod(object interface{}, method reflect.Value) (command *Co } // Construct input parameters. if len(data) > 0 { + intlog.PrintFunc(ctx, func() string { + return fmt.Sprintf(`input command data map: %s`, gjson.MustEncode(data)) + }) if inputObject.Kind() == reflect.Ptr { err = gconv.Scan(data, inputObject.Interface()) } else { err = gconv.Struct(data, inputObject.Addr().Interface()) } + intlog.PrintFunc(ctx, func() string { + return fmt.Sprintf(`input object assigned data: %s`, gjson.MustEncode(inputObject.Interface())) + }) if err != nil { return } diff --git a/os/gcmd/gcmd_command_run.go b/os/gcmd/gcmd_command_run.go index 845900fa8..c267665dc 100644 --- a/os/gcmd/gcmd_command_run.go +++ b/os/gcmd/gcmd_command_run.go @@ -146,7 +146,10 @@ func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error) } supportedOptions[optionKey] = !arg.Orphan } - parser, err := Parse(supportedOptions, c.Strict) + parser, err := Parse(supportedOptions, ParserOption{ + CaseSensitive: c.CaseSensitive, + Strict: c.Strict, + }) if err != nil { return nil, err } diff --git a/os/gcmd/gcmd_parser.go b/os/gcmd/gcmd_parser.go index abbb6f251..9ad006b2b 100644 --- a/os/gcmd/gcmd_parser.go +++ b/os/gcmd/gcmd_parser.go @@ -21,9 +21,15 @@ import ( "github.com/gogf/gf/v2/text/gstr" ) +// ParserOption manages the parsing options. +type ParserOption struct { + CaseSensitive bool // Marks options parsing in case-sensitive way. + Strict bool // Whether stops parsing and returns error if invalid option passed. +} + // Parser for arguments. type Parser struct { - strict bool // Whether stops parsing and returns error if invalid option passed. + option ParserOption // Parse option. parsedArgs []string // As name described. parsedOptions map[string]string // As name described. passedOptions map[string]bool // User passed supported options, like: map[string]bool{"name,n":true} @@ -47,7 +53,7 @@ func ParserFromCtx(ctx context.Context) *Parser { // the value item of `supportedOptions` indicates whether corresponding option name needs argument or not. // // The optional parameter `strict` specifies whether stops parsing and returns error if invalid option passed. -func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) { +func Parse(supportedOptions map[string]bool, option ...ParserOption) (*Parser, error) { if supportedOptions == nil { command.Init(os.Args...) return &Parser{ @@ -55,7 +61,7 @@ func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) { parsedOptions: GetOptAll(), }, nil } - return ParseArgs(os.Args, supportedOptions, strict...) + return ParseArgs(os.Args, supportedOptions, option...) } // ParseArgs creates and returns a new Parser with given arguments and supported options. @@ -64,7 +70,7 @@ func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) { // the value item of `supportedOptions` indicates whether corresponding option name needs argument or not. // // The optional parameter `strict` specifies whether stops parsing and returns error if invalid option passed. -func ParseArgs(args []string, supportedOptions map[string]bool, strict ...bool) (*Parser, error) { +func ParseArgs(args []string, supportedOptions map[string]bool, option ...ParserOption) (*Parser, error) { if supportedOptions == nil { command.Init(args...) return &Parser{ @@ -72,12 +78,12 @@ func ParseArgs(args []string, supportedOptions map[string]bool, strict ...bool) parsedOptions: GetOptAll(), }, nil } - strictParsing := false - if len(strict) > 0 { - strictParsing = strict[0] + var parserOption ParserOption + if len(option) > 0 { + parserOption = option[0] } parser := &Parser{ - strict: strictParsing, + option: parserOption, parsedArgs: make([]string, 0), parsedOptions: make(map[string]string), passedOptions: supportedOptions, @@ -118,7 +124,7 @@ func ParseArgs(args []string, supportedOptions map[string]bool, strict ...bool) } i++ continue - } else if parser.strict { + } else if parser.option.Strict { return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid option '%s'`, args[i]) } } @@ -159,8 +165,18 @@ func (p *Parser) parseOption(argument string) string { } func (p *Parser) isOptionValid(name string) bool { - _, ok := p.supportedOptions[name] - return ok + // Case-Sensitive. + if p.option.CaseSensitive { + _, ok := p.supportedOptions[name] + return ok + } + // Case-InSensitive. + for optionName, _ := range p.supportedOptions { + if gstr.Equal(optionName, name) { + return true + } + } + return false } func (p *Parser) isOptionNeedArgument(name string) bool { diff --git a/os/gcmd/gcmd_z_unit_feature_object3_test.go b/os/gcmd/gcmd_z_unit_feature_object3_test.go new file mode 100644 index 000000000..e388f6fd0 --- /dev/null +++ b/os/gcmd/gcmd_z_unit_feature_object3_test.go @@ -0,0 +1,50 @@ +// 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_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 TestParamsCase struct { + g.Meta `name:"root" root:"root"` +} + +type TestParamsCaseRootInput struct { + g.Meta `name:"root"` + Name string +} +type TestParamsCaseRootOutput struct { + Content string +} + +func (c *TestParamsCase) Root(ctx context.Context, in TestParamsCaseRootInput) (out *TestParamsCaseRootOutput, err error) { + out = &TestParamsCaseRootOutput{ + Content: in.Name, + } + return +} + +func Test_Command_ParamsCase(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ctx = gctx.New() + cmd, err := gcmd.NewFromObject(TestParamsCase{}) + t.AssertNil(err) + + os.Args = []string{"root", "-name=john"} + value, err := cmd.RunWithValueError(ctx) + t.AssertNil(err) + t.Assert(value, `{"Content":"john"}`) + }) +} diff --git a/protocol/goai/goai.go b/protocol/goai/goai.go index aef00be0e..e25d6e296 100644 --- a/protocol/goai/goai.go +++ b/protocol/goai/goai.go @@ -82,8 +82,8 @@ const ( ) const ( - patternKeyForRequired = `required` - patternKeyForIn = `in:` + validationRuleKeyForRequired = `required` + validationRuleKeyForIn = `in:` ) var ( diff --git a/protocol/goai/goai_parameter_ref.go b/protocol/goai/goai_parameter_ref.go index 416ec7dcc..433956788 100644 --- a/protocol/goai/goai_parameter_ref.go +++ b/protocol/goai/goai_parameter_ref.go @@ -80,8 +80,9 @@ func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path } // Required check. - if parameter.Schema.Value != nil && parameter.Schema.Value.Pattern != "" { - if gset.NewStrSetFrom(gstr.Split(parameter.Schema.Value.Pattern, "|")).Contains(patternKeyForRequired) { + if parameter.Schema.Value != nil && parameter.Schema.Value.ValidationRules != "" { + validationRuleArray := gstr.Split(parameter.Schema.Value.ValidationRules, "|") + if gset.NewStrSetFrom(validationRuleArray).Contains(validationRuleKeyForRequired) { parameter.Required = true } } diff --git a/protocol/goai/goai_shema.go b/protocol/goai/goai_shema.go index 5f96628f7..fa9819f6c 100644 --- a/protocol/goai/goai_shema.go +++ b/protocol/goai/goai_shema.go @@ -60,6 +60,7 @@ type Schema struct { AdditionalProperties *SchemaRef `json:"additionalProperties,omitempty"` Discriminator *Discriminator `json:"discriminator,omitempty"` XExtensions XExtensions `json:"-"` + ValidationRules string `json:"-"` } func (s Schema) MarshalJSON() ([]byte, error) { @@ -183,9 +184,9 @@ func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) { } schema.Properties.Iterator(func(key string, ref SchemaRef) bool { - if ref.Value != nil && ref.Value.Pattern != "" { - validationRuleSet := gset.NewStrSetFrom(gstr.Split(ref.Value.Pattern, "|")) - if validationRuleSet.Contains(patternKeyForRequired) { + if ref.Value != nil && ref.Value.ValidationRules != "" { + validationRuleSet := gset.NewStrSetFrom(gstr.Split(ref.Value.ValidationRules, "|")) + if validationRuleSet.Contains(validationRuleKeyForRequired) { schema.Required = append(schema.Required, key) } } @@ -212,14 +213,14 @@ func (oai *OpenApiV3) tagMapToSchema(tagMap map[string]string, schema *Schema) e for _, tag := range gvalid.GetTags() { if validationTagValue, ok := tagMap[tag]; ok { _, validationRules, _ := gvalid.ParseTagValue(validationTagValue) - schema.Pattern = validationRules + schema.ValidationRules = validationRules // Enum checks. if len(schema.Enum) == 0 { for _, rule := range gstr.SplitAndTrim(validationRules, "|") { - if gstr.HasPrefix(rule, patternKeyForIn) { + if gstr.HasPrefix(rule, validationRuleKeyForIn) { var ( isAllEnumNumber = true - enumArray = gstr.SplitAndTrim(rule[len(patternKeyForIn):], ",") + enumArray = gstr.SplitAndTrim(rule[len(validationRuleKeyForIn):], ",") ) for _, enum := range enumArray { if !gstr.IsNumeric(enum) { diff --git a/protocol/goai/goai_xextensions.go b/protocol/goai/goai_xextensions.go index cc11ef6a6..1d2f2462e 100644 --- a/protocol/goai/goai_xextensions.go +++ b/protocol/goai/goai_xextensions.go @@ -11,7 +11,7 @@ import ( ) // XExtensions stores the `x-` custom extensions. -type XExtensions map[string]interface{} +type XExtensions map[string]string func (oai *OpenApiV3) tagMapToXExtensions(tagMap map[string]string, extensions XExtensions) { for k, v := range tagMap {