diff --git a/gen/http/parser/annotation/body_test.go b/gen/http/parser/annotation/body_test.go index 7cb5aae..3e9d1ca 100644 --- a/gen/http/parser/annotation/body_test.go +++ b/gen/http/parser/annotation/body_test.go @@ -47,7 +47,7 @@ func TestParseBody(t *testing.T) { { name: "invalid directive", in: "user name:xx", - wantErrStr: "invalid parameter pair: name:xx", + wantErrStr: "invalid parameter option: name:xx", }, } diff --git a/gen/http/parser/annotation/param.go b/gen/http/parser/annotation/param.go index 7203ce6..a5b3220 100644 --- a/gen/http/parser/annotation/param.go +++ b/gen/http/parser/annotation/param.go @@ -10,7 +10,12 @@ import ( ) var ( - reKokParam = regexp.MustCompile(`^(\w+)(.*)$`) + reKunParam = regexp.MustCompile(`^(\w+)(.*)$`) + + // Supported formats: + // - k1=v1 + // - k2='v2' + reKunParamOption = regexp.MustCompile(`(\w+)=('[^']+'|\S+)`) ) type Param struct { @@ -50,7 +55,7 @@ func ParseParams(s string) ([]*Param, error) { func parseParam(s string) (*Param, error) { s = strings.TrimSpace(s) - r := reKokParam.FindStringSubmatch(s) + r := reKunParam.FindStringSubmatch(s) if len(r) != 3 { return nil, fmt.Errorf("invalid directive arguments: %s", s) } @@ -88,32 +93,29 @@ func parseOption(argName, s string) (*spec.Parameter, error) { s = strings.TrimSpace(s) p := new(spec.Parameter) - for _, part := range strings.Fields(s) { - part = strings.TrimSpace(part) - kv := strings.SplitN(part, "=", 2) - if len(kv) != 2 { - return nil, fmt.Errorf("invalid parameter pair: %s", part) - } - - k, v := kv[0], kv[1] + pairs, err := parseOptionPairs(s) + if err != nil { + return nil, err + } - switch k { + for _, pair := range pairs { + switch pair.k { case "in": - p.In = spec.Location(v) + p.In = spec.Location(pair.v) if err := validateLocation(p.In); err != nil { return nil, err } case "name": - p.Name = v + p.Name = pair.v case "required": - p.Required = v == "true" + p.Required = pair.v == "true" case "type": - p.Type = v + p.Type = pair.v case "descr": - p.Description = v + p.Description = pair.v default: - return nil, fmt.Errorf("invalid directive argument: %s", part) + return nil, fmt.Errorf("invalid parameter option: %s=%s", pair.k, pair.v) } } @@ -134,6 +136,28 @@ func parseOption(argName, s string) (*spec.Parameter, error) { return p, nil } +type optionPair struct{ k, v string } + +func parseOptionPairs(s string) ([]optionPair, error) { + // NOTE: Instead of using ReplaceAllString and then FindAllStringSubmatch, + // a more performant alternative solution may be to use ReplaceAllStringFunc once. + + unmatched := strings.TrimSpace(reKunParamOption.ReplaceAllString(s, "")) + if unmatched != "" { + return nil, fmt.Errorf("invalid parameter option: %s", unmatched) + } + + var pairs []optionPair + result := reKunParamOption.FindAllStringSubmatch(s, -1) + for _, r := range result { + pairs = append(pairs, optionPair{ + k: r[1], + v: strings.Trim(strings.TrimSpace(r[2]), "'"), + }) + } + return pairs, nil +} + func validateLocation(in spec.Location) error { if in != spec.InPath && in != spec.InQuery && in != spec.InHeader && /*in != InCookie &&*/ in != spec.InRequest { diff --git a/gen/http/parser/annotation/param_test.go b/gen/http/parser/annotation/param_test.go index 5f3f89a..a9d3949 100644 --- a/gen/http/parser/annotation/param_test.go +++ b/gen/http/parser/annotation/param_test.go @@ -17,7 +17,7 @@ func TestParseParams(t *testing.T) { }{ { name: "one binding one sub-parameter", - in: "name in=header name=X-User-Name required=true type=string descr=user-name", + in: "name in=header name=X-User-Name required=true type=string descr='user name'", wantOut: []*annotation.Param{ { ArgName: "name", @@ -27,7 +27,7 @@ func TestParseParams(t *testing.T) { Name: "X-User-Name", Required: true, Type: "string", - Description: "user-name", + Description: "user name", }, }, }, @@ -107,15 +107,20 @@ func TestParseParams(t *testing.T) { }, }, { - name: "invalid parameter pair", + name: "invalid parameter option", in: "name x:y", - wantErrStr: "invalid parameter pair: x:y", + wantErrStr: "invalid parameter option: x:y", }, { name: "invalid location", in: "name in=xxx", wantErrStr: `invalid location value: xxx (must be "path", "query", "header" or "request")`, }, + { + name: "invalid parameter option key", + in: "name xxx=yyy", + wantErrStr: "invalid parameter option: xxx=yyy", + }, } for _, tt := range tests {