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 }