From f643890564104999ef6bd5ab6b65af2450a98dae Mon Sep 17 00:00:00 2001 From: Alexander Evgin Date: Wed, 7 Jan 2026 17:31:32 +0400 Subject: [PATCH 1/3] feature(protoc-gen-prost-serde): add "feature" option --- protoc-gen-prost-serde/README.md | 3 +++ protoc-gen-prost-serde/src/generator.rs | 13 ++++++++++++- protoc-gen-prost-serde/src/lib.rs | 14 +++++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/protoc-gen-prost-serde/README.md b/protoc-gen-prost-serde/README.md index 0ec1d3d..38d1c47 100644 --- a/protoc-gen-prost-serde/README.md +++ b/protoc-gen-prost-serde/README.md @@ -48,6 +48,9 @@ In addition, the following options can also be specified: by `protoc-gen-prost`. This behavior may be desired if this plugin is run in a separate `protoc` invocation and you encounter a `Tried to insert into file that doesn't exist` error. +* `feature(=)`: Adds feature gate to all generated includes. If `no_include` + is set, this option has no effect. If the `` value is not specified, + default feature name `"serde"` will be used. A note on parameter values: diff --git a/protoc-gen-prost-serde/src/generator.rs b/protoc-gen-prost-serde/src/generator.rs index 210f754..7c6b445 100644 --- a/protoc-gen-prost-serde/src/generator.rs +++ b/protoc-gen-prost-serde/src/generator.rs @@ -8,6 +8,7 @@ pub struct PbJsonGenerator { builder: pbjson_build::Builder, prefixes: Vec, insert_include: bool, + feature_name: Option, } impl Generator for PbJsonGenerator { @@ -27,6 +28,11 @@ impl Generator for PbJsonGenerator { if self.insert_include { res.push(request.append_to_file(|buf| { + if let Some(feature_name) = &self.feature_name { + buf.push_str("#[cfg(feature = "); + buf.push_str(feature_name); + buf.push_str(")]"); + } buf.push_str("include!(\""); buf.push_str(&output_filename); buf.push_str("\");\n"); @@ -55,11 +61,16 @@ impl Generator for PbJsonGenerator { } impl PbJsonGenerator { - pub fn new(builder: pbjson_build::Builder, insert_include: bool) -> Self { + pub fn new( + builder: pbjson_build::Builder, + insert_include: bool, + feature_name: Option, + ) -> Self { Self { builder, prefixes: vec![".".to_owned()], insert_include, + feature_name, } } } diff --git a/protoc-gen-prost-serde/src/lib.rs b/protoc-gen-prost-serde/src/lib.rs index 0972d45..6cbd05a 100644 --- a/protoc-gen-prost-serde/src/lib.rs +++ b/protoc-gen-prost-serde/src/lib.rs @@ -28,7 +28,8 @@ pub fn execute(raw_request: &[u8]) -> protoc_gen_prost::Result { params.flat_output_dir, )?; - let files = PbJsonGenerator::new(builder, !params.no_include).generate(&module_request_set)?; + let files = PbJsonGenerator::new(builder, !params.no_include, params.feature) + .generate(&module_request_set)?; Ok(files) } @@ -49,6 +50,7 @@ struct Parameters { btree_map: Vec, flat_output_dir: bool, exclude: Vec, + feature: Option, } impl Parameters { @@ -93,6 +95,9 @@ impl Parameters { } } +/// This name will be used as a gate feature name when "feature" parameter is passed without value +const DEFAULT_FEATURE_NAME: &str = "serde"; + impl str::FromStr for Parameters { type Err = InvalidParameter; fn from_str(s: &str) -> Result { @@ -184,6 +189,13 @@ impl str::FromStr for Parameters { param: "exclude", value: prefix, } => ret_val.exclude.push(prefix.to_string()), + Param::Parameter { param: "feature" } => { + ret_val.feature = Some(DEFAULT_FEATURE_NAME.to_string()) + } + Param::Value { + param: "feature", + value, + } => ret_val.feature = Some(value.to_string()), _ => return Err(InvalidParameter::from(param)), } } From f6c8ce1acd8f25451173365f9e86368ec0f46f3a Mon Sep 17 00:00:00 2001 From: Alexander Evgin Date: Sun, 18 Jan 2026 19:24:06 +0100 Subject: [PATCH 2/3] Fix generated attirbute --- protoc-gen-prost-serde/src/generator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protoc-gen-prost-serde/src/generator.rs b/protoc-gen-prost-serde/src/generator.rs index 7c6b445..0f94b39 100644 --- a/protoc-gen-prost-serde/src/generator.rs +++ b/protoc-gen-prost-serde/src/generator.rs @@ -29,9 +29,9 @@ impl Generator for PbJsonGenerator { if self.insert_include { res.push(request.append_to_file(|buf| { if let Some(feature_name) = &self.feature_name { - buf.push_str("#[cfg(feature = "); + buf.push_str("#[cfg(feature = \""); buf.push_str(feature_name); - buf.push_str(")]"); + buf.push_str("\")]\n"); } buf.push_str("include!(\""); buf.push_str(&output_filename); From c184f74a60fb40c6fb7e154a19ad6fa497d6d34c Mon Sep 17 00:00:00 2001 From: Alexander Evgin Date: Sun, 18 Jan 2026 19:25:58 +0100 Subject: [PATCH 3/3] Add "feature" option into example code --- example/build-with-buf/Cargo.toml | 5 +++-- example/build-with-buf/buf.gen.yaml | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/example/build-with-buf/Cargo.toml b/example/build-with-buf/Cargo.toml index b020f30..ef0dd8a 100644 --- a/example/build-with-buf/Cargo.toml +++ b/example/build-with-buf/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" edition = "2021" [features] -default = ["proto_full"] +default = ["proto_full", "serde"] +serde = ["dep:serde"] # @@protoc_deletion_point(features) # This section is automatically generated by protoc-gen-prost-crate. # Changes in this area may be lost on regeneration. @@ -17,7 +18,7 @@ bytes = "1.10.1" prost = "0.14.1" pbjson = "0.8.0" pbjson-types = "0.8.0" -serde = "1.0.228" +serde = { version = "1.0.228", optional = true } tonic = { version = "0.14.2", features = ["gzip"] } tonic-prost = "0.14.2" diff --git a/example/build-with-buf/buf.gen.yaml b/example/build-with-buf/buf.gen.yaml index bfc07fd..2464c9f 100644 --- a/example/build-with-buf/buf.gen.yaml +++ b/example/build-with-buf/buf.gen.yaml @@ -12,6 +12,8 @@ plugins: - local: protoc-gen-prost-serde #path: ../../target/debug/protoc-gen-prost-serde out: src/gen + opt: + - feature - local: protoc-gen-tonic #path: ../../target/debug/protoc-gen-tonic out: src/gen