protobuf_codegen/gen/
scope.rs

1use std::ops::Deref;
2
3use protobuf::reflect::EnumDescriptor;
4use protobuf::reflect::EnumValueDescriptor;
5use protobuf::reflect::FieldDescriptor;
6use protobuf::reflect::FileDescriptor;
7use protobuf::reflect::MessageDescriptor;
8use protobuf::reflect::OneofDescriptor;
9use protobuf_parse::ProtobufAbsPath;
10use protobuf_parse::ProtobufAbsPathRef;
11use protobuf_parse::ProtobufIdentRef;
12use protobuf_parse::ProtobufRelPath;
13use protobuf_parse::ProtobufRelPathRef;
14
15use crate::customize::Customize;
16use crate::gen::field::rust_field_name_for_protobuf_field_name;
17use crate::gen::file_and_mod::FileAndMod;
18use crate::gen::map::map_entry;
19use crate::gen::message::message_name_to_nested_mod_name;
20use crate::gen::paths::proto_path_to_rust_mod;
21use crate::gen::rust::ident::RustIdent;
22use crate::gen::rust::ident_with_path::RustIdentWithPath;
23use crate::gen::rust::rel_path::RustRelativePath;
24use crate::gen::strx::capitalize;
25
26pub(crate) struct RootScope<'a> {
27    pub file_descriptors: &'a [FileDescriptor],
28}
29
30impl<'a> RootScope<'a> {
31    fn packages(&'a self) -> Vec<FileScope<'a>> {
32        self.file_descriptors
33            .iter()
34            .map(|fd| FileScope {
35                file_descriptor: fd,
36            })
37            .collect()
38    }
39
40    // find enum by fully qualified name
41    pub fn _find_enum(&'a self, fqn: &ProtobufAbsPath) -> EnumWithScope<'a> {
42        match self.find_message_or_enum(fqn) {
43            MessageOrEnumWithScope::Enum(e) => e,
44            _ => panic!("not an enum: {}", fqn),
45        }
46    }
47
48    // find message by fully qualified name
49    pub fn find_message(&'a self, fqn: &ProtobufAbsPath) -> MessageWithScope<'a> {
50        match self.find_message_or_enum(fqn) {
51            MessageOrEnumWithScope::Message(m) => m,
52            _ => panic!("not a message: {}", fqn),
53        }
54    }
55
56    // find message or enum by fully qualified name
57    pub fn find_message_or_enum(&'a self, fqn: &ProtobufAbsPath) -> MessageOrEnumWithScope<'a> {
58        assert!(!fqn.is_root());
59        self.packages()
60            .into_iter()
61            .flat_map(|p| p.find_message_or_enum_abs(fqn))
62            .next()
63            .expect(&format!("enum not found by name: {}", fqn))
64    }
65}
66
67#[derive(Clone, Debug)]
68pub(crate) struct FileScope<'a> {
69    pub file_descriptor: &'a FileDescriptor,
70}
71
72impl<'a> Deref for FileScope<'a> {
73    type Target = FileDescriptor;
74
75    fn deref(&self) -> &Self::Target {
76        self.file_descriptor
77    }
78}
79
80impl<'a> FileScope<'a> {
81    fn package(&self) -> ProtobufAbsPath {
82        ProtobufAbsPath::package_from_file_descriptor(self.file_descriptor)
83    }
84
85    pub fn to_scope(&self) -> Scope<'a> {
86        Scope {
87            file_scope: self.clone(),
88            path: Vec::new(),
89        }
90    }
91
92    fn find_message_or_enum(
93        &self,
94        name: &ProtobufRelPathRef,
95    ) -> Option<MessageOrEnumWithScope<'a>> {
96        self.find_messages_and_enums()
97            .into_iter()
98            .filter(|e| e.protobuf_name_to_package().as_ref() == name)
99            .next()
100    }
101
102    fn find_message_or_enum_abs(
103        &self,
104        name: &ProtobufAbsPathRef,
105    ) -> Option<MessageOrEnumWithScope<'a>> {
106        let name = name.to_owned();
107        match name.remove_prefix(&self.package()) {
108            Some(rem) => self.find_message_or_enum(&rem),
109            None => None,
110        }
111    }
112
113    // find all enums in given file descriptor
114    pub fn find_enums(&self) -> Vec<EnumWithScope<'a>> {
115        let mut r = Vec::new();
116
117        self.to_scope().walk_scopes(|scope| {
118            r.extend(scope.enums());
119        });
120
121        r
122    }
123
124    /// Find all messages in given file descriptor
125    pub fn find_messages(&self) -> Vec<MessageWithScope<'a>> {
126        let mut r = Vec::new();
127
128        self.to_scope().walk_scopes(|scope| {
129            r.extend(scope.messages());
130        });
131
132        r
133    }
134
135    /// Find all messages in given file descriptor, except map messages
136    pub fn find_messages_except_map(&self) -> Vec<MessageWithScope<'a>> {
137        self.find_messages()
138            .into_iter()
139            .filter(|m| !m.is_map())
140            .collect()
141    }
142
143    /// find all messages and enums in given file descriptor
144    pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
145        let mut r = Vec::new();
146
147        self.to_scope().walk_scopes(|scope| {
148            r.extend(scope.messages_and_enums());
149        });
150
151        r
152    }
153}
154
155#[derive(Clone, Debug)]
156pub(crate) struct Scope<'a> {
157    pub file_scope: FileScope<'a>,
158    pub path: Vec<MessageDescriptor>,
159}
160
161impl<'a> Scope<'a> {
162    pub(crate) fn file_descriptor(&self) -> FileDescriptor {
163        self.file_scope.file_descriptor.clone()
164    }
165
166    // get message descriptors in this scope
167    fn message_descriptors(&self) -> Vec<MessageDescriptor> {
168        if self.path.is_empty() {
169            self.file_scope.file_descriptor.messages().collect()
170        } else {
171            self.path.last().unwrap().nested_messages().collect()
172        }
173    }
174
175    // get enum descriptors in this scope
176    fn enum_descriptors(&self) -> Vec<EnumDescriptor> {
177        if self.path.is_empty() {
178            self.file_scope.file_descriptor.enums().collect()
179        } else {
180            self.path.last().unwrap().nested_enums().collect()
181        }
182    }
183
184    // get messages with attached scopes in this scope
185    pub fn messages(&self) -> Vec<MessageWithScope<'a>> {
186        self.message_descriptors()
187            .into_iter()
188            .map(|message| MessageWithScope {
189                scope: self.clone(),
190                message,
191            })
192            .collect()
193    }
194
195    // get enums with attached scopes in this scope
196    pub fn enums(&self) -> Vec<EnumWithScope<'a>> {
197        self.enum_descriptors()
198            .into_iter()
199            .map(|en| EnumWithScope {
200                scope: self.clone(),
201                en,
202            })
203            .collect()
204    }
205
206    // get messages and enums with attached scopes in this scope
207    pub fn messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
208        self.messages()
209            .into_iter()
210            .map(|m| MessageOrEnumWithScope::Message(m))
211            .chain(
212                self.enums()
213                    .into_iter()
214                    .map(|m| MessageOrEnumWithScope::Enum(m)),
215            )
216            .collect()
217    }
218
219    // nested scopes, i. e. scopes of nested messages
220    fn nested_scopes(&self) -> Vec<Scope<'a>> {
221        self.message_descriptors()
222            .into_iter()
223            .map(|m| {
224                let mut nested = self.clone();
225                nested.path.push(m);
226                nested
227            })
228            .collect()
229    }
230
231    fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) {
232        (*callback)(self);
233
234        for nested in self.nested_scopes() {
235            nested.walk_scopes_impl(callback);
236        }
237    }
238
239    // apply callback for this scope and all nested scopes
240    fn walk_scopes<F>(&self, mut callback: F)
241    where
242        F: FnMut(&Scope<'a>),
243    {
244        self.walk_scopes_impl(&mut callback);
245    }
246
247    pub fn rust_path_to_file(&self) -> RustRelativePath {
248        RustRelativePath::from_idents(
249            self.path
250                .iter()
251                .map(|m| message_name_to_nested_mod_name(m.name())),
252        )
253    }
254
255    pub fn path_str(&self) -> String {
256        let v: Vec<&str> = self.path.iter().map(|m| m.name()).collect();
257        v.join(".")
258    }
259
260    pub fn prefix(&self) -> String {
261        let path_str = self.path_str();
262        if path_str.is_empty() {
263            path_str
264        } else {
265            format!("{}.", path_str)
266        }
267    }
268
269    pub fn protobuf_path_to_file(&self) -> ProtobufRelPath {
270        ProtobufRelPath::from_components(self.path.iter().map(|m| ProtobufIdentRef::new(m.name())))
271    }
272
273    pub fn protobuf_absolute_path(&self) -> ProtobufAbsPath {
274        let mut r = self.file_scope.package();
275        r.push_relative(&self.protobuf_path_to_file());
276        r
277    }
278
279    pub fn file_and_mod(&self, customize: Customize) -> FileAndMod {
280        FileAndMod {
281            file: self.file_scope.file_descriptor.proto().name().to_owned(),
282            relative_mod: self.rust_path_to_file(),
283            customize,
284        }
285    }
286}
287
288pub(crate) trait WithScope<'a> {
289    fn scope(&self) -> &Scope<'a>;
290
291    fn file_descriptor(&self) -> FileDescriptor {
292        self.scope().file_descriptor()
293    }
294
295    // message or enum name
296    fn name(&self) -> &ProtobufIdentRef;
297
298    fn name_to_package(&self) -> String {
299        let mut r = self.scope().prefix();
300        r.push_str(&self.name());
301        r
302    }
303
304    fn protobuf_name_to_package(&self) -> ProtobufRelPath {
305        let r = self.scope().protobuf_path_to_file();
306        r.append_ident(ProtobufIdentRef::new(self.name()))
307    }
308
309    /// Return absolute name starting with dot
310    fn name_absolute(&self) -> ProtobufAbsPath {
311        let mut path = self.scope().protobuf_absolute_path();
312        path.push_simple(self.name());
313        path
314    }
315
316    // rust type name of this descriptor
317    fn rust_name(&self) -> RustIdent {
318        let rust_name = capitalize(&self.name());
319        RustIdent::new(&rust_name)
320    }
321
322    fn rust_name_to_file(&self) -> RustIdentWithPath {
323        self.scope()
324            .rust_path_to_file()
325            .into_path()
326            .with_ident(self.rust_name())
327    }
328
329    // fully-qualified name of this type
330    fn rust_name_with_file(&self) -> RustIdentWithPath {
331        let mut r = self.rust_name_to_file();
332        r.prepend_ident(proto_path_to_rust_mod(
333            self.scope().file_descriptor().name(),
334        ));
335        r
336    }
337}
338
339#[derive(Clone, Debug)]
340pub(crate) struct MessageWithScope<'a> {
341    pub scope: Scope<'a>,
342    pub message: MessageDescriptor,
343}
344
345impl<'a> WithScope<'a> for MessageWithScope<'a> {
346    fn scope(&self) -> &Scope<'a> {
347        &self.scope
348    }
349
350    fn name(&self) -> &ProtobufIdentRef {
351        ProtobufIdentRef::new(self.message.name())
352    }
353}
354
355impl<'a> MessageWithScope<'a> {
356    pub fn into_scope(mut self) -> Scope<'a> {
357        self.scope.path.push(self.message);
358        self.scope
359    }
360
361    pub fn to_scope(&self) -> Scope<'a> {
362        self.clone().into_scope()
363    }
364
365    pub fn fields(&self) -> Vec<FieldWithContext<'a>> {
366        self.message
367            .fields()
368            .into_iter()
369            .map(|field| FieldWithContext {
370                field,
371                message: self.clone(),
372            })
373            .collect()
374    }
375
376    pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> {
377        self.message
378            .oneofs()
379            .into_iter()
380            .map(|oneof| OneofWithContext {
381                message: self.clone(),
382                oneof,
383            })
384            .collect()
385    }
386
387    pub fn mod_name(&self) -> RustIdent {
388        message_name_to_nested_mod_name(self.message.name())
389    }
390
391    /// This message is a special message which is a map.
392    pub fn is_map(&self) -> bool {
393        map_entry(self).is_some()
394    }
395}
396
397#[derive(Clone, Debug)]
398pub(crate) struct EnumWithScope<'a> {
399    pub scope: Scope<'a>,
400    pub en: EnumDescriptor,
401}
402
403impl<'a> EnumWithScope<'a> {
404    pub fn values(&self) -> Vec<EnumValueWithContext<'a>> {
405        self.en
406            .values()
407            .into_iter()
408            .map(|v| EnumValueWithContext {
409                en: self.clone(),
410                proto: v,
411            })
412            .collect()
413    }
414
415    // find enum value by protobuf name
416    pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> {
417        self.values()
418            .into_iter()
419            .find(|v| v.proto.proto().name() == name)
420            .unwrap()
421    }
422}
423
424#[derive(Clone, Debug)]
425pub(crate) struct EnumValueWithContext<'a> {
426    pub en: EnumWithScope<'a>,
427    pub proto: EnumValueDescriptor,
428}
429
430impl<'a> EnumValueWithContext<'a> {
431    pub fn rust_name(&self) -> RustIdent {
432        // TODO: camel case or something.
433        RustIdent::new(self.proto.name())
434    }
435}
436
437impl<'a> WithScope<'a> for EnumWithScope<'a> {
438    fn scope(&self) -> &Scope<'a> {
439        &self.scope
440    }
441
442    fn name(&self) -> &ProtobufIdentRef {
443        ProtobufIdentRef::new(self.en.name())
444    }
445}
446
447pub(crate) enum MessageOrEnumWithScope<'a> {
448    Message(MessageWithScope<'a>),
449    Enum(EnumWithScope<'a>),
450}
451
452impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> {
453    fn scope(&self) -> &Scope<'a> {
454        match self {
455            MessageOrEnumWithScope::Message(m) => m.scope(),
456            MessageOrEnumWithScope::Enum(e) => e.scope(),
457        }
458    }
459
460    fn name(&self) -> &ProtobufIdentRef {
461        match self {
462            MessageOrEnumWithScope::Message(m) => m.name(),
463            MessageOrEnumWithScope::Enum(e) => e.name(),
464        }
465    }
466}
467
468#[derive(Clone)]
469pub(crate) struct FieldWithContext<'a> {
470    pub field: FieldDescriptor,
471    pub message: MessageWithScope<'a>,
472}
473
474impl<'a> Deref for FieldWithContext<'a> {
475    type Target = FieldDescriptor;
476
477    fn deref(&self) -> &Self::Target {
478        &self.field
479    }
480}
481
482impl<'a> FieldWithContext<'a> {
483    pub fn is_oneof(&self) -> bool {
484        self.field.containing_oneof().is_some()
485    }
486
487    pub fn oneof(&self) -> Option<OneofWithContext<'a>> {
488        match self.field.containing_oneof() {
489            Some(oneof) => Some(OneofWithContext {
490                message: self.message.clone(),
491                oneof,
492            }),
493            None => None,
494        }
495    }
496}
497
498#[derive(Clone)]
499pub(crate) struct OneofVariantWithContext<'a> {
500    pub oneof: &'a OneofWithContext<'a>,
501    pub field: FieldDescriptor,
502}
503
504#[derive(Clone)]
505pub(crate) struct OneofWithContext<'a> {
506    pub oneof: OneofDescriptor,
507    pub message: MessageWithScope<'a>,
508}
509
510impl<'a> OneofWithContext<'a> {
511    pub fn field_name(&'a self) -> RustIdent {
512        return rust_field_name_for_protobuf_field_name(self.oneof.name());
513    }
514
515    // rust type name of enum
516    pub fn rust_name(&self) -> RustIdentWithPath {
517        let type_name = RustIdent::from(capitalize(self.oneof.name()));
518        self.message
519            .to_scope()
520            .rust_path_to_file()
521            .into_path()
522            .with_ident(type_name)
523    }
524
525    pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> {
526        self.message
527            .fields()
528            .into_iter()
529            .filter(|f| f.field.containing_oneof().as_ref() == Some(&self.oneof))
530            .map(|f| OneofVariantWithContext {
531                oneof: self,
532                field: f.field,
533            })
534            .collect()
535    }
536}