From 4c04500d95b28f8b53c6114560406d2a17bf4a8d Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 17 Jun 2026 14:10:11 +0200 Subject: [PATCH 1/3] feat: conditionally generate compile-time metadata for `Struct` Introduce a flag to control the inclusion of metadata like deprecation notices and documentation when building a Struct. This allows optimizing the runtime representation by omitting unnecessary details, while ensuring they are available for compile-time analysis and tooling. --- lib/src/scanner/mod.rs | 1 + lib/src/symbols/mod.rs | 2 ++ lib/src/types/structure.rs | 65 ++++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/lib/src/scanner/mod.rs b/lib/src/scanner/mod.rs index 9a2cbce42..3d5d346a7 100644 --- a/lib/src/scanner/mod.rs +++ b/lib/src/scanner/mod.rs @@ -703,6 +703,7 @@ impl<'r> Scanner<'r> { &module_root_descriptor, module_output.as_deref(), generate_fields_for_enums, + false, ); if let Some(module_output) = module_output { diff --git a/lib/src/symbols/mod.rs b/lib/src/symbols/mod.rs index f006a080b..6fddb9080 100644 --- a/lib/src/symbols/mod.rs +++ b/lib/src/symbols/mod.rs @@ -357,6 +357,7 @@ mod tests { &TestProto2::descriptor(), None, true, + false, ); assert_eq!(test.lookup("int32_zero").unwrap().ty(), Type::Integer); @@ -436,6 +437,7 @@ mod tests { &descriptor, Some(message.as_ref()), true, + false, ); assert_eq!( diff --git a/lib/src/types/structure.rs b/lib/src/types/structure.rs index 9fb9d3d94..9163242b0 100644 --- a/lib/src/types/structure.rs +++ b/lib/src/types/structure.rs @@ -347,11 +347,8 @@ impl Struct { /// be [`None`]. /// /// The `generate_fields_for_enums` controls whether the enums defined - /// by the proto will be included as fields in the structure. Enums are - /// required only at compile time, so that the compiler can look up the - /// enums by name and resolve their values, but at scan time enums are - /// not necessary because their values are already embedded in the code. - /// The scanner never asks for an enum by field index. + /// by the proto will be included in the structure. These are required + /// only at compile time, or when constant folding is disabled. /// /// Also notice that a .proto file can define enums at the top level, /// outside any message. Those enums will be handled as if they were @@ -383,6 +380,7 @@ impl Struct { msg_descriptor: &MessageDescriptor, msg: Option<&dyn MessageDyn>, generate_fields_for_enums: bool, + generate_compile_time_fields: bool, ) -> Rc { let syntax = msg_descriptor.file_descriptor().syntax(); let mut fields = Vec::new(); @@ -403,18 +401,21 @@ impl Struct { &ty, msg.and_then(|msg| fd.get_singular(msg)), generate_fields_for_enums, + generate_compile_time_fields, syntax, ), RuntimeFieldType::Repeated(ty) => Self::new_array( &ty, msg.map(|msg| fd.get_repeated(msg)), generate_fields_for_enums, + generate_compile_time_fields, ), RuntimeFieldType::Map(key_ty, value_ty) => Self::new_map( &key_ty, &value_ty, msg.map(|msg| fd.get_map(msg)), generate_fields_for_enums, + generate_compile_time_fields, syntax, ), }; @@ -438,9 +439,17 @@ impl Struct { // Index is initially zero, will be adjusted later. type_value: value, acl: Self::acl(&fd), - deprecation_notice: Self::deprecation_notice(&fd), + deprecation_notice: if generate_compile_time_fields { + Self::deprecation_notice(&fd) + } else { + None + }, number, - doc: Self::field_doc(msg_descriptor.full_name(), number), + doc: if generate_compile_time_fields { + Self::field_doc(msg_descriptor.full_name(), number) + } else { + None + }, }, )); } @@ -823,6 +832,7 @@ impl Struct { ty: &RuntimeType, value: Option, enum_as_fields: bool, + generate_compile_time_fields: bool, syntax: Syntax, ) -> TypeValue { match ty { @@ -880,12 +890,14 @@ impl Struct { msg_descriptor, value, enum_as_fields, + generate_compile_time_fields, ) } else { Self::from_proto_descriptor_and_msg( msg_descriptor, None, enum_as_fields, + generate_compile_time_fields, ) }; TypeValue::Struct(structure) @@ -897,6 +909,7 @@ impl Struct { ty: &RuntimeType, repeated: Option, enum_as_fields: bool, + generate_compile_time_fields: bool, ) -> TypeValue { let array = match ty { RuntimeType::I32 => { @@ -904,7 +917,7 @@ impl Struct { Array::Integers( repeated .into_iter() - .map(|value| value.to_i32().unwrap() as i64) + .map(|value| Self::value_as_i64(value)) .collect(), ) } else { @@ -916,7 +929,7 @@ impl Struct { Array::Integers( repeated .into_iter() - .map(|value| value.to_i64().unwrap()) + .map(|value| Self::value_as_i64(value)) .collect(), ) } else { @@ -928,7 +941,7 @@ impl Struct { Array::Integers( repeated .into_iter() - .map(|value| value.to_u32().unwrap() as i64) + .map(|value| Self::value_as_i64(value)) .collect(), ) } else { @@ -936,14 +949,23 @@ impl Struct { } } RuntimeType::U64 => { - todo!() + if let Some(repeated) = repeated { + Array::Integers( + repeated + .into_iter() + .map(|value| Self::value_as_i64(value)) + .collect(), + ) + } else { + Array::Integers(vec![]) + } } RuntimeType::F32 => { if let Some(repeated) = repeated { Array::Floats( repeated .into_iter() - .map(|value| value.to_f32().unwrap() as f64) + .map(|value| Self::value_as_f64(value)) .collect(), ) } else { @@ -955,7 +977,7 @@ impl Struct { Array::Floats( repeated .into_iter() - .map(|value| value.to_f64().unwrap()) + .map(|value| Self::value_as_f64(value)) .collect(), ) } else { @@ -967,7 +989,7 @@ impl Struct { Array::Bools( repeated .into_iter() - .map(|value| value.to_bool().unwrap()) + .map(|value| Self::value_as_bool(value)) .collect(), ) } else { @@ -1026,6 +1048,7 @@ impl Struct { msg_descriptor, value, enum_as_fields, + generate_compile_time_fields, ) }) .collect(), @@ -1036,6 +1059,7 @@ impl Struct { msg_descriptor, None, enum_as_fields, + generate_compile_time_fields, ), ]) } @@ -1050,6 +1074,7 @@ impl Struct { value_ty: &RuntimeType, map: Option, enum_as_fields: bool, + generate_compile_time_fields: bool, syntax: Syntax, ) -> TypeValue { let map = match key_ty { @@ -1057,6 +1082,7 @@ impl Struct { value_ty, map, enum_as_fields, + generate_compile_time_fields, syntax, ), RuntimeType::I32 @@ -1066,6 +1092,7 @@ impl Struct { value_ty, map, enum_as_fields, + generate_compile_time_fields, syntax, ), ty => { @@ -1080,6 +1107,7 @@ impl Struct { value_ty: &RuntimeType, map: Option, enum_as_fields: bool, + generate_compile_time_fields: bool, syntax: Syntax, ) -> Map { if let Some(map) = map { @@ -1091,6 +1119,7 @@ impl Struct { value_ty, Some(value), enum_as_fields, + generate_compile_time_fields, syntax, ), ); @@ -1102,6 +1131,7 @@ impl Struct { value_ty, None, enum_as_fields, + generate_compile_time_fields, syntax, )), map: Default::default(), @@ -1113,6 +1143,7 @@ impl Struct { value_ty: &RuntimeType, map: Option, enum_as_fields: bool, + generate_compile_time_fields: bool, syntax: Syntax, ) -> Map { if let Some(map) = map { @@ -1124,6 +1155,7 @@ impl Struct { value_ty, Some(value), enum_as_fields, + generate_compile_time_fields, syntax, ), ); @@ -1135,6 +1167,7 @@ impl Struct { value_ty, None, enum_as_fields, + generate_compile_time_fields, syntax, )), map: Default::default(), @@ -1146,12 +1179,14 @@ impl Struct { msg_descriptor: &MessageDescriptor, value: ReflectValueRef, enum_as_fields: bool, + generate_compile_time_fields: bool, ) -> Rc { if let ReflectValueRef::Message(m) = value { Struct::from_proto_descriptor_and_msg( msg_descriptor, Some(m.deref()), enum_as_fields, + generate_compile_time_fields, ) } else { unreachable!() @@ -1226,6 +1261,7 @@ impl From<&dyn RegisteredModule> for Rc { &module.root_descriptor(), None, true, + true, ); // Get a mutable reference for the module's structure. This is @@ -1308,6 +1344,7 @@ mod tests { &TestProto2::descriptor(), None, true, + true, ); let structure = Rc::::get_mut(&mut structure).unwrap(); From 9255a3eb447bb9f91f30889b0d811b56719465ce Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 17 Jun 2026 15:42:52 +0200 Subject: [PATCH 2/3] perf: Optimize `StructField` metadata storage and serialization Mark `acl`, `deprecation_notice`, and `doc` fields as `#[serde(skip)]` to prevent their serialization, as they are primarily compile-time concerns. Change `doc` to `Option<&str>` to eliminate heap allocations for documentation strings. Extend the conditional generation of `acl` based on the `generate_compile_time_fields` flag, further reducing runtime memory footprint when this metadata is not required. --- lib/src/types/structure.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/src/types/structure.rs b/lib/src/types/structure.rs index 9163242b0..460c09448 100644 --- a/lib/src/types/structure.rs +++ b/lib/src/types/structure.rs @@ -99,12 +99,15 @@ pub(crate) struct StructField { /// Field type and value. pub type_value: TypeValue, /// Access control list (ACL) for accessing this struct field. + #[serde(skip)] pub acl: Option>, /// Deprecation notice that must be shown when the field is used in a /// rule. This is `None` for non-deprecated fields. + #[serde(skip)] pub deprecation_notice: Option, /// Description of the field extracted from the .proto file. - pub doc: Option, + #[serde(skip)] + pub doc: Option<&'static str>, } /// A dynamic structure with one or more fields. @@ -438,7 +441,11 @@ impl Struct { StructField { // Index is initially zero, will be adjusted later. type_value: value, - acl: Self::acl(&fd), + acl: if generate_compile_time_fields { + Self::acl(&fd) + } else { + None + }, deprecation_notice: if generate_compile_time_fields { Self::deprecation_notice(&fd) } else { @@ -797,7 +804,7 @@ impl Struct { }) } - fn field_doc(msg_name: &str, field_number: u64) -> Option { + fn field_doc(msg_name: &str, field_number: u64) -> Option<&'static str> { use crate::modules::field_docs::FIELD_DOCS; let idx = FIELD_DOCS .binary_search_by(|&(name, number, _)| match name.cmp(msg_name) { @@ -805,7 +812,7 @@ impl Struct { ord => ord, }) .ok()?; - Some(FIELD_DOCS[idx].2.to_string()) + Some(FIELD_DOCS[idx].2) } /// Given a protobuf type and value returns a [`TypeValue`]. From 75d63945b7f7f812737b55311327dca88df5f8fa Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 17 Jun 2026 16:02:02 +0200 Subject: [PATCH 3/3] style: fix clippy warning. --- lib/src/modules/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/modules/mod.rs b/lib/src/modules/mod.rs index d6dbcdfed..0d7dab5bb 100644 --- a/lib/src/modules/mod.rs +++ b/lib/src/modules/mod.rs @@ -492,7 +492,7 @@ pub mod mods { /// Returns the documentation for the current field. pub fn doc(&self) -> Option<&str> { - self.struct_field.doc.as_deref() + self.struct_field.doc } }