diff --git a/tools/goctl/api/format/format.go b/tools/goctl/api/format/format.go index eb9d1326..bef40ca0 100644 --- a/tools/goctl/api/format/format.go +++ b/tools/goctl/api/format/format.go @@ -65,7 +65,7 @@ func ApiFormat(path string, printToConsole bool) error { return m }) - apiStruct, err := parser.MatchStruct(r) + apiStruct, err := parser.ParseApi(r) if err != nil { return err } diff --git a/tools/goctl/api/new/newservice.go b/tools/goctl/api/new/newservice.go index b180cf56..3bfba3b0 100644 --- a/tools/goctl/api/new/newservice.go +++ b/tools/goctl/api/new/newservice.go @@ -20,9 +20,7 @@ type Response struct { } service {{.name}}-api { - @server( - handler: GreetHandler - ) + @handler GreetHandler get /greet/from/:name(Request) returns (Response); } ` diff --git a/tools/goctl/api/parser/entity.go b/tools/goctl/api/parser/entity.go index b905bc9b..bb505eae 100644 --- a/tools/goctl/api/parser/entity.go +++ b/tools/goctl/api/parser/entity.go @@ -84,6 +84,18 @@ memberLoop: if builder.Len() == 0 { return errors.New("invalid annotation format") } + if len(annoName) > 0 { + value := builder.String() + if value != string(leftParenthesis) { + builder.Reset() + annos = append(annos, spec.Annotation{ + Name: annoName, + Value: value, + }) + annoName = "" + break annotationLoop + } + } case next == leftParenthesis: if builder.Len() == 0 { return errors.New("invalid annotation format") @@ -101,6 +113,7 @@ memberLoop: Name: annoName, Properties: attrs, }) + annoName = "" break annotationLoop default: builder.WriteRune(next) diff --git a/tools/goctl/api/parser/parser.go b/tools/goctl/api/parser/parser.go index b3c32589..064767c4 100644 --- a/tools/goctl/api/parser/parser.go +++ b/tools/goctl/api/parser/parser.go @@ -16,6 +16,7 @@ import ( type Parser struct { r *bufio.Reader typeDef string + api *ApiStruct } func NewParser(filename string) (*Parser, error) { @@ -29,7 +30,7 @@ func NewParser(filename string) (*Parser, error) { return nil, err } - apiStruct, err := MatchStruct(string(api)) + apiStruct, err := ParseApi(string(api)) if err != nil { return nil, err } @@ -55,6 +56,7 @@ func NewParser(filename string) (*Parser, error) { return &Parser{ r: bufio.NewReader(buffer), typeDef: apiStruct.StructBody, + api: apiStruct, }, nil } @@ -66,7 +68,7 @@ func (p *Parser) Parse() (api *spec.ApiSpec, err error) { return nil, err } api.Types = types - var lineNumber = 1 + var lineNumber = p.api.serviceBeginLine st := newRootState(p.r, &lineNumber) for { st, err = st.process(api) diff --git a/tools/goctl/api/parser/parser_test.go b/tools/goctl/api/parser/parser_test.go index e2b96a2c..6df80950 100644 --- a/tools/goctl/api/parser/parser_test.go +++ b/tools/goctl/api/parser/parser_test.go @@ -104,6 +104,21 @@ service A-api } ` +const anonymousAnnotation = ` +type Request struct { + Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` +} + +type Response struct { + Message string ` + "`" + `json:"message"` + "`" + ` +} + +service A-api { + @handler GreetHandler + get /greet/from/:name(Request) returns (Response) +} +` + func TestParser(t *testing.T) { filename := "greet.api" err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm) @@ -161,6 +176,25 @@ func TestInvalidApiFile(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - _, err = NewParser(filename) + parser, err := NewParser(filename) + assert.Nil(t, err) + + _, err = parser.Parse() assert.NotNil(t, err) } + +func TestAnonymousAnnotation(t *testing.T) { + filename := "greet.api" + err := ioutil.WriteFile(filename, []byte(anonymousAnnotation), os.ModePerm) + assert.Nil(t, err) + defer os.Remove(filename) + + parser, err := NewParser(filename) + assert.Nil(t, err) + + api, err := parser.Parse() + assert.Nil(t, err) + + assert.Equal(t, len(api.Service.Routes), 1) + assert.Equal(t, api.Service.Routes[0].Annotations[0].Value, "GreetHandler") +} diff --git a/tools/goctl/api/parser/util.go b/tools/goctl/api/parser/util.go index 6c5413ef..63b6caf4 100644 --- a/tools/goctl/api/parser/util.go +++ b/tools/goctl/api/parser/util.go @@ -11,10 +11,11 @@ import ( var emptyType spec.Type type ApiStruct struct { - Info string - StructBody string - Service string - Imports string + Info string + StructBody string + Service string + Imports string + serviceBeginLine int } func GetType(api *spec.ApiSpec, t string) spec.Type { @@ -69,7 +70,7 @@ func unread(r *bufio.Reader) error { return r.UnreadRune() } -func MatchStruct(api string) (*ApiStruct, error) { +func ParseApi(api string) (*ApiStruct, error) { var result ApiStruct scanner := bufio.NewScanner(strings.NewReader(api)) var parseInfo = false @@ -104,13 +105,13 @@ func MatchStruct(api string) (*ApiStruct, error) { parseType = true } if isServiceBeginLine(line) { + parseService = true if parseType { parseType = false result.StructBody = segment segment = line + "\n" continue } - parseService = true } segment += scanner.Text() + "\n" } @@ -119,6 +120,7 @@ func MatchStruct(api string) (*ApiStruct, error) { return nil, errors.New("no service defined") } result.Service = segment + result.serviceBeginLine = lineBeginOfService(api) return &result, nil } @@ -133,3 +135,16 @@ func isTypeBeginLine(line string) bool { func isServiceBeginLine(line string) bool { return strings.HasPrefix(line, "@server(") || (strings.HasPrefix(line, "service") && strings.HasSuffix(line, "{")) } + +func lineBeginOfService(api string) int { + scanner := bufio.NewScanner(strings.NewReader(api)) + var number = 0 + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if isServiceBeginLine(line) { + break + } + number++ + } + return number +} diff --git a/tools/goctl/api/spec/spec.go b/tools/goctl/api/spec/spec.go index a311e4de..6c019371 100644 --- a/tools/goctl/api/spec/spec.go +++ b/tools/goctl/api/spec/spec.go @@ -4,6 +4,7 @@ type ( Annotation struct { Name string Properties map[string]string + Value string } ApiSpec struct { diff --git a/tools/goctl/api/util/annotation.go b/tools/goctl/api/util/annotation.go index 982a48b1..196c6b6f 100644 --- a/tools/goctl/api/util/annotation.go +++ b/tools/goctl/api/util/annotation.go @@ -8,6 +8,9 @@ import ( func GetAnnotationValue(annos []spec.Annotation, key, field string) (string, bool) { for _, anno := range annos { + if anno.Name == field && len(anno.Value) > 0 { + return anno.Value, true + } if anno.Name == key { value, ok := anno.Properties[field] return strings.TrimSpace(value), ok