package main import ( "fmt" "strings" api_options "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" ) func (g *Generator) microGenerate(component string, plugin *protogen.Plugin) error { for _, file := range plugin.Files { if !file.Generate { continue } if len(file.Services) == 0 { continue } gname := file.GeneratedFilenamePrefix + "_" + component + ".pb.go" gfile := plugin.NewGeneratedFile(gname, ".") gfile.P("// Code generated by protoc-gen-micro") gfile.P("// source: ", *file.Proto.Name) gfile.P("package ", file.GoPackageName) gfile.QualifiedGoIdent(protogen.GoIdent{"context", "context"}) gfile.QualifiedGoIdent(protogen.GoIdent{"api", "github.com/unistack-org/micro/v3/api"}) gfile.QualifiedGoIdent(protogen.GoIdent{"client", "github.com/unistack-org/micro/v3/client"}) gfile.QualifiedGoIdent(protogen.GoIdent{"server", "github.com/unistack-org/micro/v3/server"}) gfile.P("// Reference imports to suppress errors if they are not otherwise used.") gfile.P("var (") gfile.P("_ ", "api.Endpoint") gfile.P("_ ", "context.Context") gfile.P(" _ ", "client.Option") gfile.P(" _ ", "server.Option") gfile.P(")") gfile.P() // generate services for _, service := range file.Services { generateServiceEndpoints(gfile, service) generateServiceClientInterface(gfile, service) generateServiceClientStreamInterface(gfile, service) generateServiceServerInterface(gfile, service) generateServiceServerStreamInterface(gfile, service) } } return nil } func generateServerSignature(serviceName string, method *protogen.Method) string { methodName := string(method.GoName) req := []string{"ctx context.Context"} ret := "error" if !method.Desc.IsStreamingClient() { req = append(req, "req *"+string(method.Input.Desc.Name())) } if method.Desc.IsStreamingServer() || method.Desc.IsStreamingClient() { req = append(req, "stream "+serviceName+"_"+methodName+"Stream") } if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { req = append(req, "rsp *"+string(method.Output.Desc.Name())) } return methodName + "(" + strings.Join(req, ", ") + ") " + ret } func generateClientSignature(serviceName string, method *protogen.Method) string { methodName := string(method.GoName) req := ", req *" + string(method.Input.Desc.Name()) if method.Desc.IsStreamingClient() { req = "" } rsp := "*" + string(method.Output.Desc.Name()) if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { rsp = serviceName + "_" + methodName + "Service" } return fmt.Sprintf("%s(ctx context.Context%s, opts ...client.CallOption) (%s, error)", methodName, req, rsp) } func generateServiceClientInterface(gfile *protogen.GeneratedFile, service *protogen.Service) { serviceName := strings.TrimSuffix(service.GoName, "Service") gfile.P("type ", serviceName, "Service interface {") for _, method := range service.Methods { gfile.P(generateClientSignature(serviceName, method)) } gfile.P("}") gfile.P() } func generateServiceServerInterface(gfile *protogen.GeneratedFile, service *protogen.Service) { serviceName := strings.TrimSuffix(service.GoName, "Service") gfile.P("type ", serviceName, "Handler interface {") for _, method := range service.Methods { gfile.P(generateServerSignature(serviceName, method)) } gfile.P("}") gfile.P() } func generateServiceClientStreamInterface(gfile *protogen.GeneratedFile, service *protogen.Service) { serviceName := strings.TrimSuffix(service.GoName, "Service") for _, method := range service.Methods { if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { continue } methodName := method.GoName gfile.P("type ", serviceName, "_", methodName, "Service interface {") gfile.P("Context() context.Context") gfile.P("SendMsg(msg interface{}) error") gfile.P("RecvMsg(msg interface{}) error") if method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { gfile.P("RecvAndClose() (", method.Output.Desc.Name(), ", error)") } gfile.P("Close() error") if method.Desc.IsStreamingClient() { gfile.P("Send(msg *", method.Input.Desc.Name(), ") error") } if method.Desc.IsStreamingServer() { gfile.P("Recv() (msg *", method.Output.Desc.Name(), ", error)") } } } func generateServiceServerStreamInterface(gfile *protogen.GeneratedFile, service *protogen.Service) { serviceName := strings.TrimSuffix(service.GoName, "Service") for _, method := range service.Methods { if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { continue } methodName := method.GoName gfile.P("type ", serviceName, "_", methodName, "Stream interface {") gfile.P("Context() context.Context") gfile.P("SendMsg(msg interface{}) error") gfile.P("RecvMsg(msg interface{}) error") if method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { gfile.P("SendAndClose(msg *", method.Output.Desc.Name(), ") error") } gfile.P("Close() error") if method.Desc.IsStreamingClient() { gfile.P("Send(msg *", method.Output.Desc.Name(), ") error") } if method.Desc.IsStreamingServer() { gfile.P("Recv() (msg *", method.Input.Desc.Name(), ", error)") } } } func generateServiceEndpoints(gfile *protogen.GeneratedFile, service *protogen.Service) { serviceName := strings.TrimSuffix(service.GoName, "Service") gfile.P("//New", serviceName, "Endpoints provides api endpoints metdata for ", serviceName, " service") gfile.P("func New", serviceName, "Endpoints() []*api.Endpoint {") gfile.P("return []*", "api.Endpoint{") for _, method := range service.Methods { if method.Desc.Options() == nil { continue } if proto.HasExtension(method.Desc.Options(), api_options.E_Http) { endpoints, streaming := generateEndpoints(method) for _, endpoint := range endpoints { gfile.P("&", "api.Endpoint{") generateEndpoint(gfile, serviceName, method.GoName, endpoint, streaming) gfile.P("},") } } } gfile.P("}") gfile.P("}") gfile.P() } func generateEndpoints(method *protogen.Method) ([]*api_options.HttpRule, bool) { if method.Desc.Options() == nil { return nil, false } if !proto.HasExtension(method.Desc.Options(), api_options.E_Http) { return nil, false } r := proto.GetExtension(method.Desc.Options(), api_options.E_Http) if r == nil { return nil, false } rule := r.(*api_options.HttpRule) rules := []*api_options.HttpRule{rule} rules = append(rules, rule.GetAdditionalBindings()...) return rules, method.Desc.IsStreamingServer() || method.Desc.IsStreamingClient() } func generateEndpoint(gfile *protogen.GeneratedFile, serviceName string, methodName string, rule *api_options.HttpRule, streaming bool) { var meth string var path string switch { case len(rule.GetDelete()) > 0: meth = "DELETE" path = rule.GetDelete() case len(rule.GetGet()) > 0: meth = "GET" path = rule.GetGet() case len(rule.GetPatch()) > 0: meth = "PATCH" path = rule.GetPatch() case len(rule.GetPost()) > 0: meth = "POST" path = rule.GetPost() case len(rule.GetPut()) > 0: meth = "PUT" path = rule.GetPut() case rule.GetCustom() != nil: crule := rule.GetCustom() meth = crule.Kind path = crule.Path } if len(meth) == 0 || len(path) == 0 { return } gfile.P("Name:", fmt.Sprintf(`"%s.%s",`, serviceName, methodName)) gfile.P("Path:", fmt.Sprintf(`[]string{"%s"},`, path)) gfile.P("Method:", fmt.Sprintf(`[]string{"%s"},`, meth)) if len(rule.GetGet()) == 0 { gfile.P("Body:", fmt.Sprintf(`"%s",`, rule.GetBody())) } if streaming { gfile.P("Stream: true,") } gfile.P(`Handler: "rpc",`) return }