1 //! Oneof-related codegen functions. 2 3 use std::collections::HashSet; 4 5 use protobuf::descriptor::field_descriptor_proto; 6 use protobuf::descriptor::file_options; 7 use protobuf::reflect::FieldDescriptor; 8 use protobuf_parse::ProtobufAbsPath; 9 10 use crate::customize::ctx::CustomizeElemCtx; 11 use crate::customize::Customize; 12 use crate::gen::code_writer::CodeWriter; 13 use crate::gen::code_writer::Visibility; 14 use crate::gen::field::elem::FieldElem; 15 use crate::gen::field::rust_variant_name_for_protobuf_oneof_field_name; 16 use crate::gen::field::FieldGen; 17 use crate::gen::file_and_mod::FileAndMod; 18 use crate::gen::inside::protobuf_crate_path; 19 use crate::gen::message::MessageGen; 20 use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof; 21 use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof_field; 22 use crate::gen::rust::ident::RustIdent; 23 use crate::gen::rust::ident_with_path::RustIdentWithPath; 24 use crate::gen::rust::path::RustPath; 25 use crate::gen::rust::rel_path::RustRelativePath; 26 use crate::gen::rust_types_values::make_path; 27 use crate::gen::rust_types_values::RustType; 28 use crate::gen::scope::OneofVariantWithContext; 29 use crate::gen::scope::OneofWithContext; 30 use crate::gen::scope::RootScope; 31 use crate::gen::scope::WithScope; 32 33 // oneof one { ... } 34 #[derive(Clone)] 35 pub(crate) struct OneofField<'a> { 36 pub elem: FieldElem<'a>, 37 pub oneof_variant_rust_name: RustIdent, 38 pub oneof_field_name: RustIdent, 39 pub type_name: RustIdentWithPath, 40 pub boxed: bool, 41 } 42 43 impl<'a> OneofField<'a> { 44 // Detecting recursion: if oneof fields contains a self-reference 45 // or another message which has a reference to self, 46 // put oneof variant into a box. need_boxed( field: &FieldDescriptor, root_scope: &RootScope, owner_name: &ProtobufAbsPath, ) -> bool47 fn need_boxed( 48 field: &FieldDescriptor, 49 root_scope: &RootScope, 50 owner_name: &ProtobufAbsPath, 51 ) -> bool { 52 let mut visited_messages = HashSet::new(); 53 let mut fields = vec![field.clone()]; 54 while let Some(field) = fields.pop() { 55 if field.proto().type_() == field_descriptor_proto::Type::TYPE_MESSAGE { 56 let message_name = ProtobufAbsPath::from(field.proto().type_name()); 57 if !visited_messages.insert(message_name.clone()) { 58 continue; 59 } 60 if message_name == *owner_name { 61 return true; 62 } 63 let message = root_scope.find_message(&message_name); 64 fields.extend( 65 message 66 .message 67 .fields() 68 .into_iter() 69 .filter(|f| f.containing_oneof().is_some()), 70 ); 71 } 72 } 73 false 74 } 75 parse( oneof: &OneofWithContext<'a>, field: &FieldDescriptor, elem: FieldElem<'a>, root_scope: &RootScope, ) -> OneofField<'a>76 pub fn parse( 77 oneof: &OneofWithContext<'a>, 78 field: &FieldDescriptor, 79 elem: FieldElem<'a>, 80 root_scope: &RootScope, 81 ) -> OneofField<'a> { 82 let boxed = OneofField::need_boxed(field, root_scope, &oneof.message.name_absolute()); 83 84 OneofField { 85 elem, 86 type_name: oneof.rust_name(), 87 boxed, 88 oneof_variant_rust_name: rust_variant_name_for_protobuf_oneof_field_name(field.name()), 89 oneof_field_name: oneof.field_name(), 90 } 91 } 92 rust_type(&self, reference: &FileAndMod) -> RustType93 pub fn rust_type(&self, reference: &FileAndMod) -> RustType { 94 let t = self.elem.rust_storage_elem_type(reference); 95 96 if self.boxed { 97 RustType::Uniq(Box::new(t)) 98 } else { 99 t 100 } 101 } 102 variant_path(&self, reference: &RustRelativePath) -> RustIdentWithPath103 pub fn variant_path(&self, reference: &RustRelativePath) -> RustIdentWithPath { 104 make_path( 105 reference, 106 &self 107 .type_name 108 .to_path() 109 .with_ident(self.oneof_variant_rust_name.clone()), 110 ) 111 } 112 } 113 114 #[derive(Clone)] 115 pub(crate) struct OneofVariantGen<'a> { 116 oneof: &'a OneofGen<'a>, 117 _variant: OneofVariantWithContext<'a>, 118 oneof_field: OneofField<'a>, 119 pub field: FieldGen<'a>, 120 _path: String, 121 } 122 123 impl<'a> OneofVariantGen<'a> { parse( oneof: &'a OneofGen<'a>, variant: OneofVariantWithContext<'a>, field: &'a FieldGen, _root_scope: &RootScope, ) -> OneofVariantGen<'a>124 fn parse( 125 oneof: &'a OneofGen<'a>, 126 variant: OneofVariantWithContext<'a>, 127 field: &'a FieldGen, 128 _root_scope: &RootScope, 129 ) -> OneofVariantGen<'a> { 130 OneofVariantGen { 131 oneof, 132 _variant: variant.clone(), 133 field: field.clone(), 134 _path: format!( 135 "{}::{}", 136 oneof.type_name_relative(&oneof.oneof.message.scope.rust_path_to_file()), 137 field.rust_name 138 ), 139 oneof_field: OneofField::parse( 140 variant.oneof, 141 &field.proto_field.field, 142 field.elem().clone(), 143 oneof.message.root_scope, 144 ), 145 } 146 } 147 rust_type(&self, reference: &FileAndMod) -> RustType148 pub fn rust_type(&self, reference: &FileAndMod) -> RustType { 149 self.oneof_field.rust_type(reference) 150 } 151 path(&self, reference: &FileAndMod) -> RustPath152 pub fn path(&self, reference: &FileAndMod) -> RustPath { 153 RustPath::from(format!( 154 "{}::{}", 155 self.oneof.type_name_relative(&reference.relative_mod), 156 self.oneof_field.oneof_variant_rust_name, 157 )) 158 } 159 elem(&self) -> &FieldElem<'_>160 pub(crate) fn elem(&self) -> &FieldElem<'_> { 161 self.field.elem() 162 } 163 } 164 165 pub(crate) struct OneofGen<'a> { 166 // Message containing this oneof 167 message: &'a MessageGen<'a>, 168 pub oneof: OneofWithContext<'a>, 169 customize: CustomizeElemCtx<'a>, 170 lite_runtime: bool, 171 } 172 173 impl<'a> OneofGen<'a> { parse( message: &'a MessageGen, oneof: OneofWithContext<'a>, parent_customize: &CustomizeElemCtx<'a>, ) -> OneofGen<'a>174 pub fn parse( 175 message: &'a MessageGen, 176 oneof: OneofWithContext<'a>, 177 parent_customize: &CustomizeElemCtx<'a>, 178 ) -> OneofGen<'a> { 179 let customize = parent_customize.child(&Customize::default(), &oneof.oneof); 180 let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| { 181 oneof 182 .message 183 .file_descriptor() 184 .proto() 185 .options 186 .optimize_for() 187 == file_options::OptimizeMode::LITE_RUNTIME 188 }); 189 OneofGen { 190 message, 191 oneof, 192 customize, 193 lite_runtime, 194 } 195 } 196 type_name_relative(&self, source: &RustRelativePath) -> RustIdentWithPath197 pub fn type_name_relative(&self, source: &RustRelativePath) -> RustIdentWithPath { 198 make_path(source, &self.oneof.rust_name()) 199 } 200 variants_except_group(&'a self) -> Vec<OneofVariantGen<'a>>201 pub fn variants_except_group(&'a self) -> Vec<OneofVariantGen<'a>> { 202 self.oneof 203 .variants() 204 .into_iter() 205 .filter_map(|v| { 206 let field = self 207 .message 208 .fields 209 .iter() 210 .filter(|f| f.proto_field.name() == v.field.name()) 211 .next() 212 .expect(&format!("field not found by name: {}", v.field.name())); 213 match field.proto_type { 214 field_descriptor_proto::Type::TYPE_GROUP => None, 215 _ => Some(OneofVariantGen::parse( 216 self, 217 v, 218 field, 219 self.message.root_scope, 220 )), 221 } 222 }) 223 .collect() 224 } 225 full_storage_type(&self) -> RustType226 pub fn full_storage_type(&self) -> RustType { 227 RustType::Option(Box::new(RustType::Oneof( 228 self.type_name_relative( 229 &self 230 .oneof 231 .message 232 .scope 233 .file_and_mod(self.customize.for_elem.clone()) 234 .relative_mod, 235 ) 236 .clone(), 237 ))) 238 } 239 file_and_mod(&self) -> FileAndMod240 fn file_and_mod(&self) -> FileAndMod { 241 let mut file_and_mod = self 242 .message 243 .message 244 .scope 245 .file_and_mod(self.customize.for_elem.clone()); 246 file_and_mod 247 .relative_mod 248 .push_ident(self.message.message.mod_name()); 249 file_and_mod 250 } 251 write_enum(&self, w: &mut CodeWriter)252 fn write_enum(&self, w: &mut CodeWriter) { 253 let derive = vec!["Clone", "PartialEq", "Debug"]; 254 w.derive(&derive); 255 w.write_line("#[non_exhaustive]"); 256 write_protoc_insertion_point_for_oneof(w, &self.customize.for_elem, &self.oneof.oneof); 257 w.pub_enum(&self.oneof.rust_name().ident.to_string(), |w| { 258 for variant in self.variants_except_group() { 259 write_protoc_insertion_point_for_oneof_field( 260 w, 261 &self.customize.for_children, 262 &variant.field.proto_field.field, 263 ); 264 w.write_line(&format!( 265 "{}({}),", 266 variant.oneof_field.oneof_variant_rust_name, 267 &variant 268 .rust_type(&self.file_and_mod()) 269 .to_code(&self.customize.for_elem) 270 )); 271 } 272 }); 273 } 274 write_impl_oneof(&self, w: &mut CodeWriter)275 fn write_impl_oneof(&self, w: &mut CodeWriter) { 276 w.impl_for_block( 277 &format!("{}::Oneof", protobuf_crate_path(&self.customize.for_elem)), 278 self.oneof.rust_name().ident.to_string(), 279 |_w| { 280 // nothing here yet 281 }, 282 ); 283 } 284 write_impl_oneof_full_fn_descriptor(&self, w: &mut CodeWriter)285 fn write_impl_oneof_full_fn_descriptor(&self, w: &mut CodeWriter) { 286 let sig = format!( 287 "descriptor() -> {}::reflect::OneofDescriptor", 288 protobuf_crate_path(&self.customize.for_elem), 289 ); 290 w.def_fn(&sig, |w| { 291 w.lazy_static( 292 "descriptor", 293 &format!( 294 "{}::reflect::OneofDescriptor", 295 protobuf_crate_path(&self.customize.for_elem), 296 ), 297 &protobuf_crate_path(&self.customize.for_elem).to_string(), 298 ); 299 let message_type = make_path( 300 &self 301 .oneof 302 .message 303 .scope() 304 .rust_path_to_file() 305 .append(self.oneof.message.mod_name().into_rel_path()), 306 &self.oneof.message.rust_name_to_file(), 307 ); 308 let expr = format!( 309 "<{} as {}::MessageFull>::descriptor().oneof_by_name(\"{}\").unwrap()", 310 message_type, 311 protobuf_crate_path(&self.customize.for_elem), 312 self.oneof.oneof.name() 313 ); 314 w.write_line(&format!("descriptor.get(|| {}).clone()", expr)); 315 }); 316 } 317 write_impl_oneof_full(&self, w: &mut CodeWriter)318 fn write_impl_oneof_full(&self, w: &mut CodeWriter) { 319 w.impl_for_block( 320 &format!( 321 "{}::OneofFull", 322 protobuf_crate_path(&self.customize.for_elem) 323 ), 324 self.oneof.rust_name().ident.to_string(), 325 |w| self.write_impl_oneof_full_fn_descriptor(w), 326 ) 327 } 328 write_generated_oneof_descriptor_data(&self, w: &mut CodeWriter)329 fn write_generated_oneof_descriptor_data(&self, w: &mut CodeWriter) { 330 let sig = format!( 331 "generated_oneof_descriptor_data() -> {}::reflect::GeneratedOneofDescriptorData", 332 protobuf_crate_path(&self.customize.for_elem) 333 ); 334 w.fn_block( 335 Visibility::Path( 336 self.oneof 337 .rust_name() 338 .path 339 .into_relative_or_panic() 340 .to_reverse(), 341 ), 342 &sig, 343 |w| { 344 w.write_line(&format!( 345 "{}::reflect::GeneratedOneofDescriptorData::new::<{}>(\"{}\")", 346 protobuf_crate_path(&self.customize.for_elem), 347 &self.oneof.rust_name().ident, 348 self.oneof.oneof.name(), 349 )); 350 }, 351 ); 352 } 353 write_impl_self(&self, w: &mut CodeWriter)354 fn write_impl_self(&self, w: &mut CodeWriter) { 355 w.impl_self_block(&format!("{}", &self.oneof.rust_name().ident), |w| { 356 if !self.lite_runtime { 357 self.write_generated_oneof_descriptor_data(w); 358 } 359 }); 360 } 361 write(&self, w: &mut CodeWriter)362 pub fn write(&self, w: &mut CodeWriter) { 363 self.write_enum(w); 364 w.write_line(""); 365 self.write_impl_oneof(w); 366 if !self.lite_runtime { 367 w.write_line(""); 368 self.write_impl_oneof_full(w); 369 } 370 w.write_line(""); 371 self.write_impl_self(w); 372 } 373 } 374