github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/cmd/internal/svc/codegen/grpcproto.go (about)

     1  package codegen
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"github.com/sirupsen/logrus"
     7  	"github.com/unionj-cloud/go-doudou/cmd/internal/astutils"
     8  	v3 "github.com/unionj-cloud/go-doudou/cmd/internal/protobuf/v3"
     9  	"github.com/unionj-cloud/go-doudou/toolkit/templateutils"
    10  	"go/ast"
    11  	"go/parser"
    12  	"go/token"
    13  	"os"
    14  	"path/filepath"
    15  	"sort"
    16  	"strings"
    17  	"text/template"
    18  )
    19  
    20  var protoTmpl = `/**
    21  * Generated by go-doudou {{.Version}}.
    22  * Don't edit!
    23  *
    24  * Version No.: {{.ProtoVer}}
    25  */
    26  syntax = "proto3";
    27  
    28  package {{.Package}};
    29  option go_package = "{{.GoPackage}}";
    30  {{ range $i := .Imports }}
    31  import "{{$i}}";
    32  {{- end }}
    33  {{ range $e := .Enums }}
    34  enum {{$e.Name}} {
    35    {{- range $f := $e.Fields }}
    36    {{$f.Name}} = {{$f.Number}};
    37    {{- end }}
    38  }
    39  {{- end }}
    40  
    41  {{- range $m := .Messages }}
    42  {{ Eval "Message" $m }}
    43  {{- end }}
    44  
    45  {{- define "Message"}}
    46  {{- if .Comments }}
    47  {{ toComment .Comments }}
    48  {{- end }}
    49  message {{.Name}} {
    50    {{- range $f := .Fields }}
    51    {{- if $f.Type.Inner}}
    52    {{ Eval "Message" $f.Type }}
    53    {{- end }}
    54    {{- if $f.Comments }}
    55    {{ toComment $f.Comments }}
    56    {{- end }}
    57    {{$f.Type}} {{$f.Name}} = {{$f.Number}}{{if $f.JsonName}} [json_name="{{$f.JsonName}}"]{{end}};
    58    {{- end }}
    59  }
    60  {{- end}}
    61  
    62  service {{.Name}} {
    63    {{- range $r := .Rpcs }}
    64    {{- if $r.Comments }}
    65    {{ toComment $r.Comments }}
    66    {{- end }}
    67    rpc {{$r.Name}}({{$r.Request}}) returns ({{$r.Response}});
    68    {{- end}}
    69  }
    70  `
    71  
    72  func toComment(comments []string) string {
    73  	if len(comments) == 0 {
    74  		return ""
    75  	}
    76  	var b strings.Builder
    77  	for i := range comments {
    78  		b.WriteString(fmt.Sprintf("// %s\n", comments[i]))
    79  	}
    80  	return strings.TrimSuffix(b.String(), "\n")
    81  }
    82  
    83  func GenGrpcProto(dir string, ic astutils.InterfaceCollector) (service v3.Service, protoFile string) {
    84  	var (
    85  		err       error
    86  		svcname   string
    87  		fi        os.FileInfo
    88  		tpl       *template.Template
    89  		f         *os.File
    90  		modfile   string
    91  		modf      *os.File
    92  		modName   string
    93  		firstLine string
    94  		grpcDir   string
    95  	)
    96  	grpcDir = filepath.Join(dir, "transport/grpc")
    97  	if err = os.MkdirAll(grpcDir, os.ModePerm); err != nil {
    98  		panic(err)
    99  	}
   100  	svcname = ic.Interfaces[0].Name
   101  	protoFile = filepath.Join(grpcDir, strings.ToLower(svcname)+".proto")
   102  	if fi, err = os.Stat(protoFile); err != nil && !os.IsNotExist(err) {
   103  		panic(err)
   104  	}
   105  	if fi != nil {
   106  		logrus.Warningln("file " + protoFile + " will be overwritten")
   107  	}
   108  	if f, err = os.Create(protoFile); err != nil {
   109  		panic(err)
   110  	}
   111  	defer f.Close()
   112  	modfile = filepath.Join(dir, "go.mod")
   113  	if modf, err = Open(modfile); err != nil {
   114  		panic(err)
   115  	}
   116  	reader := bufio.NewReader(modf)
   117  	firstLine, _ = reader.ReadString('\n')
   118  	modName = strings.TrimSpace(strings.TrimPrefix(firstLine, "module"))
   119  
   120  	service = v3.NewService(svcname, modName+"/transport/grpc")
   121  	service.Comments = ic.Interfaces[0].Comments
   122  	for _, method := range ic.Interfaces[0].Methods {
   123  		service.Rpcs = append(service.Rpcs, v3.NewRpc(method))
   124  	}
   125  	for k := range v3.ImportStore {
   126  		service.Imports = append(service.Imports, k)
   127  	}
   128  	sort.Strings(service.Imports)
   129  	for _, v := range v3.MessageStore {
   130  		service.Messages = append(service.Messages, v)
   131  	}
   132  	sort.SliceStable(service.Messages, func(i, j int) bool {
   133  		return service.Messages[i].Name < service.Messages[j].Name
   134  	})
   135  	for _, v := range v3.EnumStore {
   136  		service.Enums = append(service.Enums, v)
   137  	}
   138  	sort.SliceStable(service.Enums, func(i, j int) bool {
   139  		return service.Enums[i].Name < service.Enums[j].Name
   140  	})
   141  	tpl = template.New("proto.tmpl")
   142  	funcMap := make(map[string]interface{})
   143  	funcMap["toComment"] = toComment
   144  	funcMap["Eval"] = templateutils.Eval(tpl)
   145  	if tpl, err = tpl.Funcs(funcMap).Parse(protoTmpl); err != nil {
   146  		panic(err)
   147  	}
   148  	if err = tpl.Execute(f, service); err != nil {
   149  		panic(err)
   150  	}
   151  	return
   152  }
   153  
   154  func messagesOf(vofile string) []v3.Message {
   155  	fset := token.NewFileSet()
   156  	root, err := parser.ParseFile(fset, vofile, nil, parser.ParseComments)
   157  	if err != nil {
   158  		panic(err)
   159  	}
   160  	sc := astutils.NewStructCollector(ExprStringP)
   161  	ast.Walk(sc, root)
   162  	structs := sc.DocFlatEmbed()
   163  	var ret []v3.Message
   164  	for _, item := range structs {
   165  		ret = append(ret, v3.NewMessage(item))
   166  	}
   167  	return ret
   168  }
   169  
   170  func ParseVoGrpc(dir string) {
   171  	var (
   172  		err        error
   173  		messages   []v3.Message
   174  		allMethods map[string][]astutils.MethodMeta
   175  		allConsts  map[string][]string
   176  	)
   177  	vodir := filepath.Join(dir, "vo")
   178  	var files []string
   179  	err = filepath.Walk(vodir, astutils.Visit(&files))
   180  	if err != nil {
   181  		panic(err)
   182  	}
   183  	for _, file := range files {
   184  		v3.MessageNames = append(v3.MessageNames, getSchemaNames(file)...)
   185  	}
   186  	allMethods = make(map[string][]astutils.MethodMeta)
   187  	allConsts = make(map[string][]string)
   188  	for _, file := range files {
   189  		methods, consts := enumsOf(file)
   190  		for k, v := range methods {
   191  			allMethods[k] = append(allMethods[k], v...)
   192  		}
   193  		for k, v := range consts {
   194  			allConsts[k] = append(allConsts[k], v...)
   195  		}
   196  	}
   197  	for k, v := range allMethods {
   198  		if astutils.IsEnum(v) {
   199  			v3.EnumStore[k] = v3.NewEnum(astutils.EnumMeta{
   200  				Name:   k,
   201  				Values: allConsts[k],
   202  			})
   203  		}
   204  	}
   205  	for _, file := range files {
   206  		messages = append(messages, messagesOf(file)...)
   207  	}
   208  	for _, item := range messages {
   209  		v3.MessageStore[item.Name] = item
   210  	}
   211  }