• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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