diff --git a/tools/goctl/api/parser/parser.go b/tools/goctl/api/parser/parser.go index 8aac8f9c..95fcdae2 100644 --- a/tools/goctl/api/parser/parser.go +++ b/tools/goctl/api/parser/parser.go @@ -178,12 +178,9 @@ func (p parser) fieldToMember(field *ast.TypeField) spec.Member { tag := "" if !field.IsAnonymous { name = field.Name.Text() - if field.Tag == nil { - panic(fmt.Sprintf("error: line %d:%d field %s has no tag", - field.Name.Line(), field.Name.Column(), field.Name.Text())) + if field.Tag != nil { + tag = field.Tag.Text() } - - tag = field.Tag.Text() } return spec.Member{ Name: name, diff --git a/tools/goctl/pkg/parser/api/parser/analyzer.go b/tools/goctl/pkg/parser/api/parser/analyzer.go index eebc6073..f8f8a81d 100644 --- a/tools/goctl/pkg/parser/api/parser/analyzer.go +++ b/tools/goctl/pkg/parser/api/parser/analyzer.go @@ -2,6 +2,7 @@ package parser import ( "fmt" + "sort" "strings" "github.com/zeromicro/go-zero/tools/goctl/api/spec" @@ -101,7 +102,25 @@ func (a *Analyzer) convert2Spec() error { return err } - return a.fillService() + if err := a.fillService(); err != nil { + return err + } + sort.SliceStable(a.spec.Types, func(i, j int) bool { + return a.spec.Types[i].Name() < a.spec.Types[j].Name() + }) + + groups := make([]spec.Group, 0, len(a.spec.Service.Groups)) + for _, v := range a.spec.Service.Groups { + sort.SliceStable(v.Routes, func(i, j int) bool { + return v.Routes[i].Path < v.Routes[j].Path + }) + groups = append(groups, v) + } + sort.SliceStable(groups, func(i, j int) bool { + return groups[i].Annotation.Properties["group"] < groups[j].Annotation.Properties["group"] + }) + a.spec.Service.Groups = groups + return nil } func (a *Analyzer) convertAtDoc(atDoc ast.AtDocStmt) spec.AtDoc { diff --git a/tools/goctl/pkg/parser/api/parser/api.go b/tools/goctl/pkg/parser/api/parser/api.go index a1b961b6..c67c5b53 100644 --- a/tools/goctl/pkg/parser/api/parser/api.go +++ b/tools/goctl/pkg/parser/api/parser/api.go @@ -31,18 +31,40 @@ func convert2API(a *ast.AST, importManager map[string]placeholder.Type) (*API, e one := a.Stmts[0] syntax, ok := one.(*ast.SyntaxStmt) if !ok { - return nil, ast.SyntaxError(one.Pos(), "expected syntax statement, got <%T>", one) + syntax = &ast.SyntaxStmt{ + Syntax: ast.NewTokenNode(token.Token{ + Type: token.IDENT, + Text: token.Syntax, + }), + Assign: ast.NewTokenNode(token.Token{ + Type: token.ASSIGN, + Text: "=", + }), + Value: ast.NewTokenNode(token.Token{ + Type: token.STRING, + Text: `"v1"`, + }), + } } - api.Syntax = syntax - for i := 1; i < len(a.Stmts); i++ { + api.Syntax = syntax + var hasSyntax, hasInfo bool + for i := 0; i < len(a.Stmts); i++ { one := a.Stmts[i] switch val := one.(type) { case *ast.SyntaxStmt: - return nil, ast.DuplicateStmtError(val.Pos(), "duplicate syntax statement") + if hasSyntax { + return nil, ast.DuplicateStmtError(val.Pos(), "duplicate syntax statement") + } else { + hasSyntax = true + } case *ast.InfoStmt: if api.info != nil { - return nil, ast.DuplicateStmtError(val.Pos(), "duplicate info statement") + if hasInfo { + return nil, ast.DuplicateStmtError(val.Pos(), "duplicate info statement") + } + } else { + hasInfo = true } api.info = val case ast.ImportStmt: diff --git a/tools/goctl/pkg/parser/api/parser/testdata/invalid.api b/tools/goctl/pkg/parser/api/parser/testdata/invalid.api index 3f0106d4..cf58be84 100644 --- a/tools/goctl/pkg/parser/api/parser/testdata/invalid.api +++ b/tools/goctl/pkg/parser/api/parser/testdata/invalid.api @@ -1,7 +1,3 @@ -// test case: expected syntax statement -info () - ------ // test case: duplicate syntax statement syntax = "v1" syntax = "v1"