123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- package parser
- import (
- "errors"
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "github.com/emicklei/proto"
- "github.com/tal-tech/go-zero/core/collection"
- "github.com/tal-tech/go-zero/core/lang"
- "github.com/tal-tech/go-zero/tools/goctl/templatex"
- "github.com/tal-tech/go-zero/tools/goctl/util"
- "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
- )
- const (
- AnyImport = "google/protobuf/any.proto"
- )
- var (
- enumTypeTemplate = `{{.name}} int32`
- enumTemplate = `const (
- {{.element}}
- )`
- enumFiledTemplate = `{{.key}} {{.name}} = {{.value}}`
- )
- type (
- MessageField struct {
- Type string
- Name stringx.String
- }
- Message struct {
- Name stringx.String
- Element []*MessageField
- *proto.Message
- }
- Enum struct {
- Name stringx.String
- Element []*EnumField
- *proto.Enum
- }
- EnumField struct {
- Key string
- Value int
- }
- Proto struct {
- Package string
- Import []*Import
- PbSrc string
- ContainsAny bool
- Message map[string]lang.PlaceholderType
- Enum map[string]*Enum
- }
- Import struct {
- ProtoImportName string
- PbImportName string
- OriginalDir string
- OriginalProtoPath string
- OriginalPbPath string
- BridgeImport string
- exists bool
- //xx.proto
- protoName string
- // xx.pb.go
- pbName string
- }
- )
- func checkImport(src string) error {
- r, err := os.Open(src)
- if err != nil {
- return err
- }
- defer r.Close()
- parser := proto.NewParser(r)
- parseRet, err := parser.Parse()
- if err != nil {
- return err
- }
- var base = filepath.Base(src)
- proto.Walk(parseRet, proto.WithImport(func(i *proto.Import) {
- if err != nil {
- return
- }
- err = fmt.Errorf("%v:%v the external proto cannot import other proto files", base, i.Position.Line)
- }))
- if err != nil {
- return err
- }
- return nil
- }
- func ParseImport(src string) ([]*Import, bool, error) {
- bridgeImportM := make(map[string]string)
- r, err := os.Open(src)
- if err != nil {
- return nil, false, err
- }
- defer r.Close()
- workDir := filepath.Dir(src)
- parser := proto.NewParser(r)
- parseRet, err := parser.Parse()
- if err != nil {
- return nil, false, err
- }
- protoImportSet := collection.NewSet()
- var containsAny bool
- proto.Walk(parseRet, proto.WithImport(func(i *proto.Import) {
- if i.Filename == AnyImport {
- containsAny = true
- return
- }
- protoImportSet.AddStr(i.Filename)
- if i.Comment != nil {
- lines := i.Comment.Lines
- for _, line := range lines {
- line = strings.TrimSpace(line)
- if !strings.HasPrefix(line, "@") {
- continue
- }
- line = strings.TrimPrefix(line, "@")
- bridgeImportM[i.Filename] = line
- }
- }
- }))
- var importList []*Import
- for _, item := range protoImportSet.KeysStr() {
- pb := strings.TrimSuffix(filepath.Base(item), filepath.Ext(item)) + ".pb.go"
- var pbImportName, brideImport string
- if v, ok := bridgeImportM[item]; ok {
- pbImportName = v
- brideImport = "M" + item + "=" + v
- } else {
- pbImportName = item
- }
- var impo = Import{
- ProtoImportName: item,
- PbImportName: pbImportName,
- BridgeImport: brideImport,
- }
- protoSource := filepath.Join(workDir, item)
- pbSource := filepath.Join(filepath.Dir(protoSource), pb)
- if util.FileExists(protoSource) && util.FileExists(pbSource) {
- impo.OriginalProtoPath = protoSource
- impo.OriginalPbPath = pbSource
- impo.OriginalDir = filepath.Dir(protoSource)
- impo.exists = true
- impo.protoName = filepath.Base(item)
- impo.pbName = pb
- } else {
- return nil, false, fmt.Errorf("「%v」: import must be found in the relative directory of 「%v」", item, filepath.Base(src))
- }
- importList = append(importList, &impo)
- }
- return importList, containsAny, nil
- }
- func parseProto(src string, messageM map[string]lang.PlaceholderType, enumM map[string]*Enum) (*Proto, error) {
- if !filepath.IsAbs(src) {
- return nil, fmt.Errorf("expected absolute path,but found: %v", src)
- }
- r, err := os.Open(src)
- if err != nil {
- return nil, err
- }
- defer r.Close()
- parser := proto.NewParser(r)
- parseRet, err := parser.Parse()
- if err != nil {
- return nil, err
- }
- // xx.proto
- fileBase := filepath.Base(src)
- var resp Proto
- proto.Walk(parseRet, proto.WithPackage(func(p *proto.Package) {
- if err != nil {
- return
- }
- if len(resp.Package) != 0 {
- err = fmt.Errorf("%v:%v duplicate package「%v」", fileBase, p.Position.Line, p.Name)
- }
- if len(p.Name) == 0 {
- err = errors.New("package not found")
- }
- resp.Package = p.Name
- }), proto.WithMessage(func(message *proto.Message) {
- if err != nil {
- return
- }
- for _, item := range message.Elements {
- switch item.(type) {
- case *proto.NormalField, *proto.MapField, *proto.Comment:
- continue
- default:
- err = fmt.Errorf("%v: unsupport inline declaration", fileBase)
- return
- }
- }
- name := stringx.From(message.Name)
- if _, ok := messageM[name.Lower()]; ok {
- err = fmt.Errorf("%v:%v duplicate message 「%v」", fileBase, message.Position.Line, message.Name)
- return
- }
- messageM[name.Lower()] = lang.Placeholder
- }), proto.WithEnum(func(enum *proto.Enum) {
- if err != nil {
- return
- }
- var node Enum
- node.Enum = enum
- node.Name = stringx.From(enum.Name)
- for _, item := range enum.Elements {
- v, ok := item.(*proto.EnumField)
- if !ok {
- continue
- }
- node.Element = append(node.Element, &EnumField{
- Key: v.Name,
- Value: v.Integer,
- })
- }
- if _, ok := enumM[node.Name.Lower()]; ok {
- err = fmt.Errorf("%v:%v duplicate enum 「%v」", fileBase, node.Position.Line, node.Name.Source())
- return
- }
- lower := stringx.From(enum.Name).Lower()
- enumM[lower] = &node
- }))
- if err != nil {
- return nil, err
- }
- resp.Message = messageM
- resp.Enum = enumM
- return &resp, nil
- }
- func (e *Enum) GenEnumCode() (string, error) {
- var element []string
- for _, item := range e.Element {
- code, err := item.GenEnumFieldCode(e.Name.Source())
- if err != nil {
- return "", err
- }
- element = append(element, code)
- }
- buffer, err := templatex.With("enum").Parse(enumTemplate).Execute(map[string]interface{}{
- "element": strings.Join(element, util.NL),
- })
- if err != nil {
- return "", err
- }
- return buffer.String(), nil
- }
- func (e *Enum) GenEnumTypeCode() (string, error) {
- buffer, err := templatex.With("enumAlias").Parse(enumTypeTemplate).Execute(map[string]interface{}{
- "name": e.Name.Source(),
- })
- if err != nil {
- return "", err
- }
- return buffer.String(), nil
- }
- func (e *EnumField) GenEnumFieldCode(parentName string) (string, error) {
- buffer, err := templatex.With("enumField").Parse(enumFiledTemplate).Execute(map[string]interface{}{
- "key": e.Key,
- "name": parentName,
- "value": e.Value,
- })
- if err != nil {
- return "", err
- }
- return buffer.String(), nil
- }
|