diff --git a/cmd/protoc-gen-elixir-grpc/README.md b/cmd/protoc-gen-elixir-grpc/README.md index 0f08745..0841260 100644 --- a/cmd/protoc-gen-elixir-grpc/README.md +++ b/cmd/protoc-gen-elixir-grpc/README.md @@ -108,6 +108,33 @@ defmodule Greeter.Server do end ``` +#### Custom Compressors + +Specify custom compressor modules for your gRPC server: + +```yaml +version: v2 +plugins: + - local: protoc-gen-elixir + out: lib + - local: protoc-gen-elixir-grpc + out: lib + opt: + - compressors=GRPC.Compressor.Gzip +``` + +This generates: + +```elixir +defmodule Greeter.Server do + use GRPC.Server, + service: Greeter.Service, + compressors: [GRPC.Compressor.Gzip] + + # ... method delegates +end +``` + #### Combining Options You can combine multiple options: @@ -123,6 +150,7 @@ plugins: - http_transcode=true - handler_module_prefix=MyApp.Handlers - codecs=GRPC.Codec.Proto,GRPC.Codec.WebText,GRPC.Codec.JSON + - compressors=GRPC.Compressor.Gzip ``` ### Basic Service Implementation diff --git a/cmd/protoc-gen-elixir-grpc/main.go b/cmd/protoc-gen-elixir-grpc/main.go index f96c0fd..67acb61 100644 --- a/cmd/protoc-gen-elixir-grpc/main.go +++ b/cmd/protoc-gen-elixir-grpc/main.go @@ -55,8 +55,9 @@ const ( handlerModulePrefixFlag = "handler_module_prefix" httpTranscodeFlag = "http_transcode" codecsFlag = "codecs" + compressorsFlag = "compressors" - usage = "\n\nFlags:\n -h, --help\tPrint this help and exit.\n --version\tPrint the version and exit.\n --handler_module_prefix\tCustom Elixir module prefix for handler modules instead of protobuf package.\n --http_transcode\tEnable HTTP transcoding support (adds http_transcode: true to use GRPC.Server).\n --codecs\tComma-separated list of codec modules (e.g., 'GRPC.Codec.Proto,GRPC.Codec.WebText,GRPC.Codec.JSON')." + usage = "\n\nFlags:\n -h, --help\tPrint this help and exit.\n --version\tPrint the version and exit.\n --handler_module_prefix\tCustom Elixir module prefix for handler modules instead of protobuf package.\n --http_transcode\tEnable HTTP transcoding support (adds http_transcode: true to use GRPC.Server).\n --codecs\tComma-separated list of codec modules (e.g., 'GRPC.Codec.Proto,GRPC.Codec.WebText,GRPC.Codec.JSON').\n --compressors\tComma-separated list of compressor modules (e.g., 'GRPC.Compressor.Gzip')." ) func parsePluginParameters(paramStr string, flagSet *flag.FlagSet) error { @@ -178,6 +179,11 @@ func main() { "", "Comma-separated list of codec modules (e.g., 'GRPC.Codec.Proto,GRPC.Codec.WebText,GRPC.Codec.JSON').", ) + compressors := flagSet.String( + compressorsFlag, + "", + "Comma-separated list of compressor modules (e.g., 'GRPC.Compressor.Gzip').", + ) input, err := io.ReadAll(os.Stdin) if err != nil { @@ -214,6 +220,7 @@ func main() { } codecsList := parseCodecs(*codecs) + compressorsList := parseCodecs(*compressors) for _, fileName := range req.FileToGenerate { var protoFile *descriptorpb.FileDescriptorProto @@ -231,7 +238,7 @@ func main() { continue } - generateElixirFile(resp, protoFile, *packagePrefix, *handlerModulePrefix, *httpTranscode, codecsList) + generateElixirFile(resp, protoFile, *packagePrefix, *handlerModulePrefix, *httpTranscode, codecsList, compressorsList) } output, err := proto.Marshal(resp) @@ -246,7 +253,7 @@ func main() { } } -func generateElixirFile(resp *pluginpb.CodeGeneratorResponse, file *descriptorpb.FileDescriptorProto, packagePrefix, handlerModulePrefix string, httpTranscode bool, codecs []string) { +func generateElixirFile(resp *pluginpb.CodeGeneratorResponse, file *descriptorpb.FileDescriptorProto, packagePrefix, handlerModulePrefix string, httpTranscode bool, codecs []string, compressors []string) { if len(file.Service) == 0 { return } @@ -260,7 +267,7 @@ func generateElixirFile(resp *pluginpb.CodeGeneratorResponse, file *descriptorpb content.WriteString("\n") for _, service := range file.Service { - generateServiceModule(&content, file, service, handlerModulePrefix, httpTranscode, codecs) + generateServiceModule(&content, file, service, handlerModulePrefix, httpTranscode, codecs, compressors) content.WriteString("\n") } @@ -270,7 +277,7 @@ func generateElixirFile(resp *pluginpb.CodeGeneratorResponse, file *descriptorpb }) } -func generateServiceModule(content *strings.Builder, file *descriptorpb.FileDescriptorProto, service *descriptorpb.ServiceDescriptorProto, handlerModulePrefix string, httpTranscode bool, codecs []string) { +func generateServiceModule(content *strings.Builder, file *descriptorpb.FileDescriptorProto, service *descriptorpb.ServiceDescriptorProto, handlerModulePrefix string, httpTranscode bool, codecs []string, compressors []string) { serverModuleName := generateServerModuleName(file, service) serviceModuleName := generateServiceModuleName(file, service) @@ -290,6 +297,16 @@ func generateServiceModule(content *strings.Builder, file *descriptorpb.FileDesc } content.WriteString("]") } + if len(compressors) > 0 { + content.WriteString(",\n compressors: [") + for i, compressor := range compressors { + if i > 0 { + content.WriteString(", ") + } + content.WriteString(compressor) + } + content.WriteString("]") + } content.WriteString("\n\n") for _, method := range service.Method { diff --git a/cmd/protoc-gen-elixir-grpc/main_test.go b/cmd/protoc-gen-elixir-grpc/main_test.go index b1f53b1..cd87246 100644 --- a/cmd/protoc-gen-elixir-grpc/main_test.go +++ b/cmd/protoc-gen-elixir-grpc/main_test.go @@ -490,6 +490,59 @@ end assert.Equal(t, expected, content) }) + t.Run("with compressors option", func(t *testing.T) { + fileDesc := &descriptorpb.FileDescriptorProto{ + Name: ptr("data/v1/data.proto"), + Package: ptr("data.v1"), + MessageType: []*descriptorpb.DescriptorProto{ + {Name: ptr("DataRequest")}, + {Name: ptr("DataResponse")}, + }, + Service: []*descriptorpb.ServiceDescriptorProto{ + { + Name: ptr("DataService"), + Method: []*descriptorpb.MethodDescriptorProto{ + { + Name: ptr("GetData"), + InputType: ptr(".data.v1.DataRequest"), + OutputType: ptr(".data.v1.DataResponse"), + }, + }, + }, + }, + } + + req := &pluginpb.CodeGeneratorRequest{ + FileToGenerate: []string{"data/v1/data.proto"}, + ProtoFile: []*descriptorpb.FileDescriptorProto{fileDesc}, + SourceFileDescriptors: []*descriptorpb.FileDescriptorProto{fileDesc}, + CompilerVersion: compilerVersion, + Parameter: ptr("compressors=GRPC.Compressor.Gzip"), + } + + rsp := testGenerate(t, req) + assert.Nil(t, rsp.Error) + assert.Equal(t, 1, len(rsp.File)) + + file := rsp.File[0] + assert.Equal(t, "data/v1/data.server.pb.ex", file.GetName()) + + content := file.GetContent() + expected := `# Code generated by protoc-gen-elixir-grpc. DO NOT EDIT. +# +# Source: data/v1/data.proto + +defmodule Data.V1.DataService.Server do + use GRPC.Server, + service: Data.V1.DataService.Service, + compressors: [GRPC.Compressor.Gzip] + + defdelegate get_data(request, stream), to: Data.V1.DataService.Server.GetDataHandler, as: :handle_message +end +` + assert.Equal(t, expected, content) + }) + t.Run("with all options combined", func(t *testing.T) { fileDesc := &descriptorpb.FileDescriptorProto{ Name: ptr("store/v1/store.proto"), @@ -517,7 +570,7 @@ end ProtoFile: []*descriptorpb.FileDescriptorProto{fileDesc}, SourceFileDescriptors: []*descriptorpb.FileDescriptorProto{fileDesc}, CompilerVersion: compilerVersion, - Parameter: ptr("http_transcode=true,handler_module_prefix=MyApp.Business,codecs=GRPC.Codec.Proto,GRPC.Codec.JSON"), + Parameter: ptr("http_transcode=true,handler_module_prefix=MyApp.Business,codecs=GRPC.Codec.Proto,GRPC.Codec.JSON,compressors=GRPC.Compressor.Gzip"), } rsp := testGenerate(t, req) @@ -536,7 +589,8 @@ defmodule Store.V1.StoreService.Server do use GRPC.Server, service: Store.V1.StoreService.Service, http_transcode: true, - codecs: [GRPC.Codec.Proto, GRPC.Codec.JSON] + codecs: [GRPC.Codec.Proto, GRPC.Codec.JSON], + compressors: [GRPC.Compressor.Gzip] defdelegate create_store(request, stream), to: MyApp.Business.Store.V1.StoreService.Server.CreateStoreHandler, as: :handle_message end