diff --git a/prost-build/src/code_generator.rs b/prost-build/src/code_generator.rs index f8d341445..4124ee122 100644 --- a/prost-build/src/code_generator.rs +++ b/prost-build/src/code_generator.rs @@ -15,9 +15,8 @@ use prost_types::{ }; use crate::ast::{Comments, Method, Service}; -use crate::extern_paths::ExternPaths; +use crate::context::Context; use crate::ident::{strip_enum_prefix, to_snake, to_upper_camel}; -use crate::message_graph::MessageGraph; use crate::Config; mod c_escaping; @@ -26,14 +25,13 @@ use c_escaping::unescape_c_escape_string; mod syntax; use syntax::Syntax; -pub struct CodeGenerator<'a> { - config: &'a mut Config, +/// State object for the code generation process on a single input file. +pub struct CodeGenerator<'a, 'b> { + context: &'a mut Context<'b>, package: String, type_path: Vec, source_info: Option, syntax: Syntax, - message_graph: &'a MessageGraph, - extern_paths: &'a ExternPaths, depth: u8, path: Vec, buf: &'a mut String, @@ -45,10 +43,6 @@ fn push_indent(buf: &mut String, depth: u8) { } } -fn prost_path(config: &Config) -> &str { - config.prost_path.as_deref().unwrap_or("::prost") -} - struct Field { descriptor: FieldDescriptorProto, path_index: i32, @@ -87,14 +81,12 @@ impl OneofField { } } -impl CodeGenerator<'_> { - pub fn generate( - config: &mut Config, - message_graph: &MessageGraph, - extern_paths: &ExternPaths, - file: FileDescriptorProto, - buf: &mut String, - ) { +impl<'a, 'b> CodeGenerator<'a, 'b> { + fn config(&self) -> &Config { + self.context.config() + } + + pub(crate) fn generate(context: &mut Context<'b>, file: FileDescriptorProto, buf: &mut String) { let source_info = file.source_code_info.map(|mut s| { s.location.retain(|loc| { let len = loc.path.len(); @@ -105,13 +97,11 @@ impl CodeGenerator<'_> { }); let mut code_gen = CodeGenerator { - config, + context, package: file.package.unwrap_or_default(), type_path: Vec::new(), source_info, syntax: file.syntax.as_deref().into(), - message_graph, - extern_paths, depth: 0, path: Vec::new(), buf, @@ -139,7 +129,7 @@ impl CodeGenerator<'_> { } code_gen.path.pop(); - if code_gen.config.service_generator.is_some() { + if code_gen.context.service_generator().is_some() { code_gen.path.push(6); for (idx, service) in file.service.into_iter().enumerate() { code_gen.path.push(idx as i32); @@ -147,7 +137,7 @@ impl CodeGenerator<'_> { code_gen.path.pop(); } - if let Some(service_generator) = code_gen.config.service_generator.as_mut() { + if let Some(service_generator) = code_gen.context.service_generator() { service_generator.finalize(code_gen.buf); } @@ -162,7 +152,11 @@ impl CodeGenerator<'_> { let fq_message_name = self.fq_name(&message_name); // Skip external types. - if self.extern_paths.resolve_ident(&fq_message_name).is_some() { + if self + .context + .resolve_extern_ident(&fq_message_name) + .is_some() + { return; } @@ -230,12 +224,12 @@ impl CodeGenerator<'_> { self.push_indent(); self.buf.push_str(&format!( "#[derive(Clone, {}PartialEq, {}::Message)]\n", - if self.message_graph.can_message_derive_copy(&fq_message_name) { + if self.context.can_message_derive_copy(&fq_message_name) { "Copy, " } else { "" }, - prost_path(self.config) + self.context.prost_path() )); self.append_skip_debug(&fq_message_name); self.push_indent(); @@ -297,15 +291,16 @@ impl CodeGenerator<'_> { self.pop_mod(); } - if self.config.enable_type_names { + if self.config().enable_type_names { self.append_type_name(&message_name, &fq_message_name); } } fn append_type_name(&mut self, message_name: &str, fq_message_name: &str) { + let prost_path = self.context.prost_path(); + self.buf.push_str(&format!( - "impl {}::Name for {} {{\n", - self.config.prost_path.as_deref().unwrap_or("::prost"), + "impl {prost_path}::Name for {} {{\n", to_upper_camel(message_name) )); self.depth += 1; @@ -319,7 +314,6 @@ impl CodeGenerator<'_> { self.package, )); - let prost_path = self.config.prost_path.as_deref().unwrap_or("::prost"); let string_path = format!("{prost_path}::alloc::string::String"); let full_name = format!( @@ -329,11 +323,7 @@ impl CodeGenerator<'_> { self.type_path.join("."), if self.type_path.is_empty() { "" } else { "." }, ); - let domain_name = self - .config - .type_name_domains - .get_first(fq_message_name) - .map_or("", |name| name.as_str()); + let domain_name = self.context.type_name_domain(fq_message_name); self.buf.push_str(&format!( r#"fn full_name() -> {string_path} {{ "{full_name}".into() }}"#, @@ -349,7 +339,7 @@ impl CodeGenerator<'_> { fn append_type_attributes(&mut self, fq_message_name: &str) { assert_eq!(b'.', fq_message_name.as_bytes()[0]); - for attribute in self.config.type_attributes.get(fq_message_name) { + for attribute in self.context.type_attributes(fq_message_name) { push_indent(self.buf, self.depth); self.buf.push_str(attribute); self.buf.push('\n'); @@ -358,20 +348,15 @@ impl CodeGenerator<'_> { fn append_message_attributes(&mut self, fq_message_name: &str) { assert_eq!(b'.', fq_message_name.as_bytes()[0]); - for attribute in self.config.message_attributes.get(fq_message_name) { + for attribute in self.context.message_attributes(fq_message_name) { push_indent(self.buf, self.depth); self.buf.push_str(attribute); self.buf.push('\n'); } } - fn should_skip_debug(&self, fq_message_name: &str) -> bool { - assert_eq!(b'.', fq_message_name.as_bytes()[0]); - self.config.skip_debug.get(fq_message_name).next().is_some() - } - fn append_skip_debug(&mut self, fq_message_name: &str) { - if self.should_skip_debug(fq_message_name) { + if self.context.should_skip_debug(fq_message_name) { push_indent(self.buf, self.depth); self.buf.push_str("#[prost(skip_debug)]"); self.buf.push('\n'); @@ -380,7 +365,7 @@ impl CodeGenerator<'_> { fn append_enum_attributes(&mut self, fq_message_name: &str) { assert_eq!(b'.', fq_message_name.as_bytes()[0]); - for attribute in self.config.enum_attributes.get(fq_message_name) { + for attribute in self.context.enum_attributes(fq_message_name) { push_indent(self.buf, self.depth); self.buf.push_str(attribute); self.buf.push('\n'); @@ -389,11 +374,7 @@ impl CodeGenerator<'_> { fn append_field_attributes(&mut self, fq_message_name: &str, field_name: &str) { assert_eq!(b'.', fq_message_name.as_bytes()[0]); - for attribute in self - .config - .field_attributes - .get_field(fq_message_name, field_name) - { + for attribute in self.context.field_attributes(fq_message_name, field_name) { push_indent(self.buf, self.depth); self.buf.push_str(attribute); self.buf.push('\n'); @@ -405,7 +386,9 @@ impl CodeGenerator<'_> { let repeated = field.descriptor.label == Some(Label::Repeated as i32); let deprecated = self.deprecated(&field.descriptor); let optional = self.optional(&field.descriptor); - let boxed = self.boxed(&field.descriptor, fq_message_name, None); + let boxed = self + .context + .should_box_message_field(fq_message_name, &field.descriptor); let ty = self.resolve_type(&field.descriptor, fq_message_name); debug!( @@ -429,11 +412,8 @@ impl CodeGenerator<'_> { if type_ == Type::Bytes { let bytes_type = self - .config - .bytes_type - .get_first_field(fq_message_name, field.descriptor.name()) - .copied() - .unwrap_or_default(); + .context + .bytes_type(fq_message_name, field.descriptor.name()); self.buf .push_str(&format!("={:?}", bytes_type.annotation())); } @@ -477,7 +457,7 @@ impl CodeGenerator<'_> { self.buf.push_str("\\\""); } else if type_ == Type::Enum { let mut enum_value = to_upper_camel(default); - if self.config.strip_enum_prefix { + if self.config().strip_enum_prefix { // Field types are fully qualified, so we extract // the last segment and strip it from the left // side of the default value. @@ -503,7 +483,7 @@ impl CodeGenerator<'_> { self.buf.push_str(&field.rust_name()); self.buf.push_str(": "); - let prost_path = prost_path(self.config); + let prost_path = self.context.prost_path(); if repeated { self.buf @@ -546,11 +526,8 @@ impl CodeGenerator<'_> { self.push_indent(); let map_type = self - .config - .map_type - .get_first_field(fq_message_name, field.descriptor.name()) - .copied() - .unwrap_or_default(); + .context + .map_type(fq_message_name, field.descriptor.name()); let key_tag = self.field_type_tag(key); let value_tag = self.map_value_type_tag(value); @@ -616,13 +593,16 @@ impl CodeGenerator<'_> { self.push_indent(); let can_oneof_derive_copy = oneof.fields.iter().all(|field| { - self.message_graph - .can_field_derive_copy(fq_message_name, &field.descriptor) + self.context.can_oneof_field_derive_copy( + fq_message_name, + oneof.descriptor.name(), + &field.descriptor, + ) }); self.buf.push_str(&format!( "#[derive(Clone, {}PartialEq, {}::Oneof)]\n", if can_oneof_derive_copy { "Copy, " } else { "" }, - prost_path(self.config) + self.context.prost_path() )); self.append_skip_debug(fq_message_name); self.push_indent(); @@ -649,10 +629,10 @@ impl CodeGenerator<'_> { self.push_indent(); let ty = self.resolve_type(&field.descriptor, fq_message_name); - let boxed = self.boxed( - &field.descriptor, + let boxed = self.context.should_box_oneof_field( fq_message_name, - Some(oneof.descriptor.name()), + oneof.descriptor.name(), + &field.descriptor, ); debug!( @@ -693,15 +673,7 @@ impl CodeGenerator<'_> { } fn append_doc(&mut self, fq_name: &str, field_name: Option<&str>) { - let append_doc = if let Some(field_name) = field_name { - self.config - .disable_comments - .get_first_field(fq_name, field_name) - .is_none() - } else { - self.config.disable_comments.get(fq_name).next().is_none() - }; - if append_doc { + if !self.context.should_disable_comments(fq_name, field_name) { if let Some(comments) = self.location().map(Comments::from_location) { comments.append_with_indent(self.depth, self.buf); } @@ -718,8 +690,8 @@ impl CodeGenerator<'_> { let fq_proto_enum_name = self.fq_name(proto_enum_name); if self - .extern_paths - .resolve_ident(&fq_proto_enum_name) + .context + .resolve_extern_ident(&fq_proto_enum_name) .is_some() { return; @@ -729,7 +701,7 @@ impl CodeGenerator<'_> { self.append_type_attributes(&fq_proto_enum_name); self.append_enum_attributes(&fq_proto_enum_name); self.push_indent(); - let dbg = if self.should_skip_debug(&fq_proto_enum_name) { + let dbg = if self.context.should_skip_debug(&fq_proto_enum_name) { "" } else { "Debug, " @@ -737,7 +709,7 @@ impl CodeGenerator<'_> { self.buf.push_str(&format!( "#[derive(Clone, Copy, {}PartialEq, Eq, Hash, PartialOrd, Ord, {}::Enumeration)]\n", dbg, - prost_path(self.config), + self.context.prost_path(), )); self.push_indent(); self.buf.push_str("#[repr(i32)]\n"); @@ -747,7 +719,7 @@ impl CodeGenerator<'_> { self.buf.push_str(" {\n"); let variant_mappings = - build_enum_value_mappings(&enum_name, self.config.strip_enum_prefix, enum_values); + build_enum_value_mappings(&enum_name, self.config().strip_enum_prefix, enum_values); self.depth += 1; self.path.push(2); @@ -913,7 +885,7 @@ impl CodeGenerator<'_> { options: service.options.unwrap_or_default(), }; - if let Some(service_generator) = self.config.service_generator.as_mut() { + if let Some(service_generator) = self.context.service_generator() { service_generator.generate(service, self.buf) } } @@ -956,13 +928,10 @@ impl CodeGenerator<'_> { Type::Int32 | Type::Sfixed32 | Type::Sint32 | Type::Enum => String::from("i32"), Type::Int64 | Type::Sfixed64 | Type::Sint64 => String::from("i64"), Type::Bool => String::from("bool"), - Type::String => format!("{}::alloc::string::String", prost_path(self.config)), + Type::String => format!("{}::alloc::string::String", self.context.prost_path()), Type::Bytes => self - .config - .bytes_type - .get_first_field(fq_message_name, field.name()) - .copied() - .unwrap_or_default() + .context + .bytes_type(fq_message_name, field.name()) .rust_type() .to_owned(), Type::Group | Type::Message => self.resolve_ident(field.type_name()), @@ -973,7 +942,7 @@ impl CodeGenerator<'_> { // protoc should always give fully qualified identifiers. assert_eq!(".", &pb_ident[..1]); - if let Some(proto_ident) = self.extern_paths.resolve_ident(pb_ident) { + if let Some(proto_ident) = self.context.resolve_extern_ident(pb_ident) { return proto_ident; } @@ -1058,49 +1027,6 @@ impl CodeGenerator<'_> { } } - /// Returns whether the Rust type for this field needs to be `Box<_>`. - /// - /// This can be explicitly configured with `Config::boxed`, or necessary - /// to prevent an infinitely sized type definition in case when the type of - /// a non-repeated message field transitively contains the message itself. - fn boxed( - &self, - field: &FieldDescriptorProto, - fq_message_name: &str, - oneof: Option<&str>, - ) -> bool { - let repeated = field.label == Some(Label::Repeated as i32); - let fd_type = field.r#type(); - if !repeated - && (fd_type == Type::Message || fd_type == Type::Group) - && self - .message_graph - .is_nested(field.type_name(), fq_message_name) - { - return true; - } - let config_path = match oneof { - None => Cow::Borrowed(fq_message_name), - Some(ooname) => Cow::Owned(format!("{fq_message_name}.{ooname}")), - }; - if self - .config - .boxed - .get_first_field(&config_path, field.name()) - .is_some() - { - if repeated { - println!( - "cargo:warning=\ - Field X is repeated and manually marked as boxed. \ - This is deprecated and support will be removed in a later release" - ); - } - return true; - } - false - } - /// Returns `true` if the field options includes the `deprecated` option. fn deprecated(&self, field: &FieldDescriptorProto) -> bool { field diff --git a/prost-build/src/config.rs b/prost-build/src/config.rs index 896726b16..a62f682f3 100644 --- a/prost-build/src/config.rs +++ b/prost-build/src/config.rs @@ -15,6 +15,7 @@ use prost::Message; use prost_types::{FileDescriptorProto, FileDescriptorSet}; use crate::code_generator::CodeGenerator; +use crate::context::Context; use crate::extern_paths::ExternPaths; use crate::message_graph::MessageGraph; use crate::path::PathMap; @@ -1078,9 +1079,10 @@ impl Config { let mut modules = HashMap::new(); let mut packages = HashMap::new(); - let message_graph = MessageGraph::new(requests.iter().map(|x| &x.1), self.boxed.clone()); + let message_graph = MessageGraph::new(requests.iter().map(|x| &x.1)); let extern_paths = ExternPaths::new(&self.extern_paths, self.prost_types) .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?; + let mut context = Context::new(self, message_graph, extern_paths); for (request_module, request_fd) in requests { // Only record packages that have services @@ -1090,14 +1092,14 @@ impl Config { let buf = modules .entry(request_module.clone()) .or_insert_with(String::new); - CodeGenerator::generate(self, &message_graph, &extern_paths, request_fd, buf); + CodeGenerator::generate(&mut context, request_fd, buf); if buf.is_empty() { // Did not generate any code, remove from list to avoid inclusion in include file or output file list modules.remove(&request_module); } } - if let Some(ref mut service_generator) = self.service_generator { + if let Some(service_generator) = context.service_generator() { for (module, package) in packages { let buf = modules.get_mut(&module).unwrap(); service_generator.finalize_package(&package, buf); diff --git a/prost-build/src/context.rs b/prost-build/src/context.rs new file mode 100644 index 000000000..0b2234919 --- /dev/null +++ b/prost-build/src/context.rs @@ -0,0 +1,288 @@ +use std::borrow::Cow; + +use prost_types::{ + field_descriptor_proto::{Label, Type}, + FieldDescriptorProto, +}; + +use crate::extern_paths::ExternPaths; +use crate::message_graph::MessageGraph; +use crate::{BytesType, Config, MapType, ServiceGenerator}; + +/// The context providing all the global information needed to generate code. +/// It also provides a more disciplined access to Config +/// and its mutable instance of ServiceGenerator. +/// +/// A `Context` is built once in the generation process and is reused by +/// `CodeGenerator` instances created to generate code for each input file. +pub struct Context<'a> { + config: &'a mut Config, + message_graph: MessageGraph, + extern_paths: ExternPaths, +} + +impl<'a> Context<'a> { + pub fn new( + config: &'a mut Config, + message_graph: MessageGraph, + extern_paths: ExternPaths, + ) -> Self { + Self { + config, + message_graph, + extern_paths, + } + } + + pub fn config(&self) -> &Config { + self.config + } + + pub fn service_generator(&mut self) -> Option<&mut (dyn ServiceGenerator + 'static)> { + self.config.service_generator.as_deref_mut() + } + + pub fn prost_path(&self) -> &str { + self.config.prost_path.as_deref().unwrap_or("::prost") + } + + pub fn resolve_extern_ident(&self, pb_ident: &str) -> Option { + self.extern_paths.resolve_ident(pb_ident) + } + + /// Returns an iterator over the additional attributes configured + /// for the named type. + pub fn type_attributes(&self, fq_type_name: &str) -> impl Iterator { + self.config + .type_attributes + .get(fq_type_name) + .map(|s| s.as_str()) + } + + /// Returns an iterator over the additional attributes configured + /// for the named message. + pub fn message_attributes(&self, fq_message_name: &str) -> impl Iterator { + self.config + .message_attributes + .get(fq_message_name) + .map(|s| s.as_str()) + } + + /// Returns an iterator over the additional attributes configured + /// for the named enum. + pub fn enum_attributes(&self, fq_enum_name: &str) -> impl Iterator { + self.config + .enum_attributes + .get(fq_enum_name) + .map(|s| s.as_str()) + } + + /// Returns an iterator over the additional attributes configured + /// for the named message field. + pub fn field_attributes( + &self, + fq_message_name: &str, + field_name: &str, + ) -> impl Iterator { + self.config + .field_attributes + .get_field(fq_message_name, field_name) + .map(|s| s.as_str()) + } + + /// Returns the bytes type configured for the named message field. + pub(crate) fn bytes_type(&self, fq_message_name: &str, field_name: &str) -> BytesType { + self.config + .bytes_type + .get_first_field(fq_message_name, field_name) + .copied() + .unwrap_or_default() + } + + /// Returns the map type configured for the named message field. + pub(crate) fn map_type(&self, fq_message_name: &str, field_name: &str) -> MapType { + self.config + .map_type + .get_first_field(fq_message_name, field_name) + .copied() + .unwrap_or_default() + } + + /// Returns whether the Rust type for this message field needs to be `Box<_>`. + /// + /// This can be explicitly configured with `Config::boxed`, or necessary + /// to prevent an infinitely sized type definition in case when the type of + /// a non-repeated message field transitively contains the message itself. + pub fn should_box_message_field( + &self, + fq_message_name: &str, + field: &FieldDescriptorProto, + ) -> bool { + self.should_box_impl(fq_message_name, None, field) + } + + /// Returns whether the Rust type for this field in the oneof needs to be `Box<_>`. + /// + /// This can be explicitly configured with `Config::boxed`, or necessary + /// to prevent an infinitely sized type definition in case when the type of + /// a non-repeated message field transitively contains the message itself. + pub fn should_box_oneof_field( + &self, + fq_message_name: &str, + oneof_name: &str, + field: &FieldDescriptorProto, + ) -> bool { + self.should_box_impl(fq_message_name, Some(oneof_name), field) + } + + fn should_box_impl( + &self, + fq_message_name: &str, + oneof: Option<&str>, + field: &FieldDescriptorProto, + ) -> bool { + let repeated = field.label == Some(Label::Repeated as i32); + let fd_type = field.r#type(); + if !repeated + && (fd_type == Type::Message || fd_type == Type::Group) + && self + .message_graph + .is_nested(field.type_name(), fq_message_name) + { + return true; + } + let config_path = match oneof { + None => Cow::Borrowed(fq_message_name), + Some(oneof_name) => Cow::Owned(format!("{fq_message_name}.{oneof_name}")), + }; + if self + .config + .boxed + .get_first_field(&config_path, field.name()) + .is_some() + { + if repeated { + println!( + "cargo:warning=\ + Field X is repeated and manually marked as boxed. \ + This is deprecated and support will be removed in a later release" + ); + } + return true; + } + false + } + + /// Returns `true` if this message can automatically derive Copy trait. + pub fn can_message_derive_copy(&self, fq_message_name: &str) -> bool { + assert_eq!(".", &fq_message_name[..1]); + self.message_graph + .get_message(fq_message_name) + .unwrap() + .field + .iter() + .all(|field| self.can_message_field_derive_copy(fq_message_name, field)) + } + + /// Returns `true` if the type of this message field allows deriving the Copy trait. + pub fn can_message_field_derive_copy( + &self, + fq_message_name: &str, + field: &FieldDescriptorProto, + ) -> bool { + self.can_field_derive_copy_impl(fq_message_name, None, field) + } + + /// Returns `true` if the type of this field in a oneof allows deriving the Copy trait. + pub fn can_oneof_field_derive_copy( + &self, + fq_message_name: &str, + oneof_name: &str, + field: &FieldDescriptorProto, + ) -> bool { + self.can_field_derive_copy_impl(fq_message_name, Some(oneof_name), field) + } + + fn can_field_derive_copy_impl( + &self, + fq_message_name: &str, + oneof: Option<&str>, + field: &FieldDescriptorProto, + ) -> bool { + assert_eq!(".", &fq_message_name[..1]); + + // repeated field cannot derive Copy + if field.label() == Label::Repeated { + false + } else if field.r#type() == Type::Message { + // nested and boxed messages cannot derive Copy + if self + .message_graph + .is_nested(field.type_name(), fq_message_name) + { + return false; + } + let config_path = match oneof { + None => Cow::Borrowed(fq_message_name), + Some(oneof_name) => Cow::Owned(format!("{fq_message_name}.{oneof_name}")), + }; + if self + .config + .boxed + .get_first_field(&config_path, field.name()) + .is_some() + { + false + } else { + self.can_message_derive_copy(field.type_name()) + } + } else { + matches!( + field.r#type(), + Type::Float + | Type::Double + | Type::Int32 + | Type::Int64 + | Type::Uint32 + | Type::Uint64 + | Type::Sint32 + | Type::Sint64 + | Type::Fixed32 + | Type::Fixed64 + | Type::Sfixed32 + | Type::Sfixed64 + | Type::Bool + | Type::Enum + ) + } + } + + pub fn should_disable_comments(&self, fq_message_name: &str, field_name: Option<&str>) -> bool { + if let Some(field_name) = field_name { + self.config + .disable_comments + .get_first_field(fq_message_name, field_name) + .is_some() + } else { + self.config + .disable_comments + .get_first(fq_message_name) + .is_some() + } + } + + /// Returns whether the named message should skip generating the `Debug` implementation. + pub fn should_skip_debug(&self, fq_message_name: &str) -> bool { + assert_eq!(b'.', fq_message_name.as_bytes()[0]); + self.config.skip_debug.get(fq_message_name).next().is_some() + } + + /// Returns the type name domain URL for the named message, + /// or an empty string if such is not configured. + pub fn type_name_domain(&self, fq_message_name: &str) -> &str { + self.config + .type_name_domains + .get_first(fq_message_name) + .map_or("", |name| name.as_str()) + } +} diff --git a/prost-build/src/lib.rs b/prost-build/src/lib.rs index 14324f9cb..abf0f47c8 100644 --- a/prost-build/src/lib.rs +++ b/prost-build/src/lib.rs @@ -147,6 +147,7 @@ mod collections; pub(crate) use collections::{BytesType, MapType}; mod code_generator; +mod context; mod extern_paths; mod ident; mod message_graph; diff --git a/prost-build/src/message_graph.rs b/prost-build/src/message_graph.rs index e2bcad918..7d43aeceb 100644 --- a/prost-build/src/message_graph.rs +++ b/prost-build/src/message_graph.rs @@ -6,11 +6,9 @@ use petgraph::Graph; use prost_types::{ field_descriptor_proto::{Label, Type}, - DescriptorProto, FieldDescriptorProto, FileDescriptorProto, + DescriptorProto, FileDescriptorProto, }; -use crate::path::PathMap; - /// `MessageGraph` builds a graph of messages whose edges correspond to nesting. /// The goal is to recognize when message types are recursively nested, so /// that fields can be boxed when necessary. @@ -18,19 +16,14 @@ pub struct MessageGraph { index: HashMap, graph: Graph, messages: HashMap, - boxed: PathMap<()>, } impl MessageGraph { - pub(crate) fn new<'a>( - files: impl Iterator, - boxed: PathMap<()>, - ) -> MessageGraph { + pub(crate) fn new<'a>(files: impl Iterator) -> MessageGraph { let mut msg_graph = MessageGraph { index: HashMap::new(), graph: Graph::new(), messages: HashMap::new(), - boxed, }; for file in files { @@ -99,58 +92,4 @@ impl MessageGraph { has_path_connecting(&self.graph, outer, inner, None) } - - /// Returns `true` if this message can automatically derive Copy trait. - pub fn can_message_derive_copy(&self, fq_message_name: &str) -> bool { - assert_eq!(".", &fq_message_name[..1]); - self.get_message(fq_message_name) - .unwrap() - .field - .iter() - .all(|field| self.can_field_derive_copy(fq_message_name, field)) - } - - /// Returns `true` if the type of this field allows deriving the Copy trait. - pub fn can_field_derive_copy( - &self, - fq_message_name: &str, - field: &FieldDescriptorProto, - ) -> bool { - assert_eq!(".", &fq_message_name[..1]); - - // repeated field cannot derive Copy - if field.label() == Label::Repeated { - false - } else if field.r#type() == Type::Message { - // nested and boxed messages cannot derive Copy - if self.is_nested(field.type_name(), fq_message_name) - || self - .boxed - .get_first_field(fq_message_name, field.name()) - .is_some() - { - false - } else { - self.can_message_derive_copy(field.type_name()) - } - } else { - matches!( - field.r#type(), - Type::Float - | Type::Double - | Type::Int32 - | Type::Int64 - | Type::Uint32 - | Type::Uint64 - | Type::Sint32 - | Type::Sint64 - | Type::Fixed32 - | Type::Fixed64 - | Type::Sfixed32 - | Type::Sfixed64 - | Type::Bool - | Type::Enum - ) - } - } }