• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Should not be a part of public API
2 #![doc(hidden)]
3 
4 use crate::descriptor::DescriptorProto;
5 use crate::descriptor::EnumDescriptorProto;
6 use crate::descriptor::EnumValueDescriptorProto;
7 use crate::descriptor::FieldDescriptorProto;
8 /// utilities to work with descriptor
9 use crate::descriptor::FileDescriptorProto;
10 use crate::descriptor::OneofDescriptorProto;
11 use crate::rust;
12 use crate::strx;
13 
14 // Copy-pasted from libsyntax.
ident_start(c: char) -> bool15 fn ident_start(c: char) -> bool {
16     (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
17 }
18 
19 // Copy-pasted from libsyntax.
ident_continue(c: char) -> bool20 fn ident_continue(c: char) -> bool {
21     (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'
22 }
23 
proto_path_to_rust_mod(path: &str) -> String24 pub fn proto_path_to_rust_mod(path: &str) -> String {
25     let without_dir = strx::remove_to(path, '/');
26     let without_suffix = strx::remove_suffix(without_dir, ".proto");
27 
28     let name = without_suffix
29         .chars()
30         .enumerate()
31         .map(|(i, c)| {
32             let valid = if i == 0 {
33                 ident_start(c)
34             } else {
35                 ident_continue(c)
36             };
37             if valid {
38                 c
39             } else {
40                 '_'
41             }
42         })
43         .collect::<String>();
44 
45     let name = if rust::is_rust_keyword(&name) {
46         format!("{}_pb", name)
47     } else {
48         name
49     };
50     name
51 }
52 
53 pub struct RootScope<'a> {
54     pub file_descriptors: &'a [FileDescriptorProto],
55 }
56 
57 impl<'a> RootScope<'a> {
packages(&'a self) -> Vec<FileScope<'a>>58     fn packages(&'a self) -> Vec<FileScope<'a>> {
59         self.file_descriptors
60             .iter()
61             .map(|fd| FileScope {
62                 file_descriptor: fd,
63             })
64             .collect()
65     }
66 
67     // find enum by fully qualified name
find_enum(&'a self, fqn: &str) -> EnumWithScope<'a>68     pub fn find_enum(&'a self, fqn: &str) -> EnumWithScope<'a> {
69         match self.find_message_or_enum(fqn) {
70             MessageOrEnumWithScope::Enum(e) => e,
71             _ => panic!("not an enum: {}", fqn),
72         }
73     }
74 
75     // find message by fully qualified name
find_message(&'a self, fqn: &str) -> MessageWithScope<'a>76     pub fn find_message(&'a self, fqn: &str) -> MessageWithScope<'a> {
77         match self.find_message_or_enum(fqn) {
78             MessageOrEnumWithScope::Message(m) => m,
79             _ => panic!("not a message: {}", fqn),
80         }
81     }
82 
83     // find message or enum by fully qualified name
find_message_or_enum(&'a self, fqn: &str) -> MessageOrEnumWithScope<'a>84     pub fn find_message_or_enum(&'a self, fqn: &str) -> MessageOrEnumWithScope<'a> {
85         assert!(fqn.starts_with("."), "name must start with dot: {}", fqn);
86         let fqn1 = &fqn[1..];
87         self.packages()
88             .into_iter()
89             .flat_map(|p| {
90                 (if p.get_package().is_empty() {
91                     p.find_message_or_enum(fqn1)
92                 } else if fqn1.starts_with(&(p.get_package().to_string() + ".")) {
93                     let remaining = &fqn1[(p.get_package().len() + 1)..];
94                     p.find_message_or_enum(remaining)
95                 } else {
96                     None
97                 })
98                 .into_iter()
99             })
100             .next()
101             .expect(&format!("enum not found by name: {}", fqn))
102     }
103 }
104 
105 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
106 pub enum Syntax {
107     PROTO2,
108     PROTO3,
109 }
110 
111 impl Syntax {
parse(s: &str) -> Self112     pub fn parse(s: &str) -> Self {
113         match s {
114             "" | "proto2" => Syntax::PROTO2,
115             "proto3" => Syntax::PROTO3,
116             _ => panic!("unsupported syntax value: {:?}", s),
117         }
118     }
119 }
120 
121 #[derive(Clone)]
122 pub struct FileScope<'a> {
123     pub file_descriptor: &'a FileDescriptorProto,
124 }
125 
126 impl<'a> FileScope<'a> {
get_package(&self) -> &'a str127     fn get_package(&self) -> &'a str {
128         self.file_descriptor.get_package()
129     }
130 
syntax(&self) -> Syntax131     pub fn syntax(&self) -> Syntax {
132         Syntax::parse(self.file_descriptor.get_syntax())
133     }
134 
to_scope(&self) -> Scope<'a>135     pub fn to_scope(&self) -> Scope<'a> {
136         Scope {
137             file_scope: self.clone(),
138             path: Vec::new(),
139         }
140     }
141 
find_message_or_enum(&self, name: &str) -> Option<MessageOrEnumWithScope<'a>>142     fn find_message_or_enum(&self, name: &str) -> Option<MessageOrEnumWithScope<'a>> {
143         assert!(!name.starts_with("."));
144         self.find_messages_and_enums()
145             .into_iter()
146             .filter(|e| e.name_to_package() == name)
147             .next()
148     }
149 
150     // find all enums in given file descriptor
find_enums(&self) -> Vec<EnumWithScope<'a>>151     pub fn find_enums(&self) -> Vec<EnumWithScope<'a>> {
152         let mut r = Vec::new();
153 
154         self.to_scope().walk_scopes(|scope| {
155             r.extend(scope.get_enums());
156         });
157 
158         r
159     }
160 
161     // find all messages in given file descriptor
find_messages(&self) -> Vec<MessageWithScope<'a>>162     pub fn find_messages(&self) -> Vec<MessageWithScope<'a>> {
163         let mut r = Vec::new();
164 
165         self.to_scope().walk_scopes(|scope| {
166             r.extend(scope.get_messages());
167         });
168 
169         r
170     }
171 
172     // find all messages and enums in given file descriptor
find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>>173     pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
174         let mut r = Vec::new();
175 
176         self.to_scope().walk_scopes(|scope| {
177             r.extend(scope.get_messages_and_enums());
178         });
179 
180         r
181     }
182 }
183 
184 #[derive(Clone)]
185 pub struct Scope<'a> {
186     pub file_scope: FileScope<'a>,
187     pub path: Vec<&'a DescriptorProto>,
188 }
189 
190 impl<'a> Scope<'a> {
get_file_descriptor(&self) -> &'a FileDescriptorProto191     pub fn get_file_descriptor(&self) -> &'a FileDescriptorProto {
192         self.file_scope.file_descriptor
193     }
194 
195     // get message descriptors in this scope
get_message_descriptors(&self) -> &'a [DescriptorProto]196     fn get_message_descriptors(&self) -> &'a [DescriptorProto] {
197         if self.path.is_empty() {
198             self.file_scope.file_descriptor.get_message_type()
199         } else {
200             self.path.last().unwrap().get_nested_type()
201         }
202     }
203 
204     // get enum descriptors in this scope
get_enum_descriptors(&self) -> &'a [EnumDescriptorProto]205     fn get_enum_descriptors(&self) -> &'a [EnumDescriptorProto] {
206         if self.path.is_empty() {
207             self.file_scope.file_descriptor.get_enum_type()
208         } else {
209             self.path.last().unwrap().get_enum_type()
210         }
211     }
212 
213     // get messages with attached scopes in this scope
get_messages(&self) -> Vec<MessageWithScope<'a>>214     pub fn get_messages(&self) -> Vec<MessageWithScope<'a>> {
215         self.get_message_descriptors()
216             .iter()
217             .map(|m| MessageWithScope {
218                 scope: self.clone(),
219                 message: m,
220             })
221             .collect()
222     }
223 
224     // get enums with attached scopes in this scope
get_enums(&self) -> Vec<EnumWithScope<'a>>225     pub fn get_enums(&self) -> Vec<EnumWithScope<'a>> {
226         self.get_enum_descriptors()
227             .iter()
228             .map(|e| EnumWithScope {
229                 scope: self.clone(),
230                 en: e,
231             })
232             .collect()
233     }
234 
235     // get messages and enums with attached scopes in this scope
get_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>>236     pub fn get_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
237         self.get_messages()
238             .into_iter()
239             .map(|m| MessageOrEnumWithScope::Message(m))
240             .chain(
241                 self.get_enums()
242                     .into_iter()
243                     .map(|m| MessageOrEnumWithScope::Enum(m)),
244             )
245             .collect()
246     }
247 
248     // nested scopes, i. e. scopes of nested messages
nested_scopes(&self) -> Vec<Scope<'a>>249     fn nested_scopes(&self) -> Vec<Scope<'a>> {
250         self.get_message_descriptors()
251             .iter()
252             .map(|m| {
253                 let mut nested = self.clone();
254                 nested.path.push(m);
255                 nested
256             })
257             .collect()
258     }
259 
walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F)260     fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) {
261         (*callback)(self);
262 
263         for nested in self.nested_scopes() {
264             nested.walk_scopes_impl(callback);
265         }
266     }
267 
268     // apply callback for this scope and all nested scopes
walk_scopes<F>(&self, mut callback: F) where F: FnMut(&Scope<'a>),269     fn walk_scopes<F>(&self, mut callback: F)
270     where
271         F: FnMut(&Scope<'a>),
272     {
273         self.walk_scopes_impl(&mut callback);
274     }
275 
prefix(&self) -> String276     pub fn prefix(&self) -> String {
277         if self.path.is_empty() {
278             "".to_string()
279         } else {
280             let v: Vec<&'a str> = self.path.iter().map(|m| m.get_name()).collect();
281             let mut r = v.join(".");
282             r.push_str(".");
283             r
284         }
285     }
286 
287     // rust type name prefix for this scope
rust_prefix(&self) -> String288     pub fn rust_prefix(&self) -> String {
289         self.prefix().replace(".", "_")
290     }
291 }
292 
293 pub trait WithScope<'a> {
get_scope(&self) -> &Scope<'a>294     fn get_scope(&self) -> &Scope<'a>;
295 
get_file_descriptor(&self) -> &'a FileDescriptorProto296     fn get_file_descriptor(&self) -> &'a FileDescriptorProto {
297         self.get_scope().get_file_descriptor()
298     }
299 
300     // message or enum name
get_name(&self) -> &'a str301     fn get_name(&self) -> &'a str;
302 
escape_prefix(&self) -> &'static str303     fn escape_prefix(&self) -> &'static str;
304 
name_to_package(&self) -> String305     fn name_to_package(&self) -> String {
306         let mut r = self.get_scope().prefix();
307         r.push_str(self.get_name());
308         r
309     }
310 
311     /// Return absolute name starting with dot
name_absolute(&self) -> String312     fn name_absolute(&self) -> String {
313         let mut r = String::new();
314         r.push_str(".");
315         let package = self.get_file_descriptor().get_package();
316         if !package.is_empty() {
317             r.push_str(package);
318             r.push_str(".");
319         }
320         r.push_str(&self.name_to_package());
321         r
322     }
323 
324     // rust type name of this descriptor
rust_name(&self) -> String325     fn rust_name(&self) -> String {
326         let mut r = self.get_scope().rust_prefix();
327         // Only escape if prefix is not empty
328         if r.is_empty() && rust::is_rust_keyword(self.get_name()) {
329             r.push_str(self.escape_prefix());
330         }
331         r.push_str(self.get_name());
332         r
333     }
334 
335     // fully-qualified name of this type
rust_fq_name(&self) -> String336     fn rust_fq_name(&self) -> String {
337         format!(
338             "{}::{}",
339             proto_path_to_rust_mod(self.get_scope().get_file_descriptor().get_name()),
340             self.rust_name()
341         )
342     }
343 }
344 
345 #[derive(Clone)]
346 pub struct MessageWithScope<'a> {
347     pub scope: Scope<'a>,
348     pub message: &'a DescriptorProto,
349 }
350 
351 impl<'a> WithScope<'a> for MessageWithScope<'a> {
get_scope(&self) -> &Scope<'a>352     fn get_scope(&self) -> &Scope<'a> {
353         &self.scope
354     }
355 
escape_prefix(&self) -> &'static str356     fn escape_prefix(&self) -> &'static str {
357         "message_"
358     }
359 
get_name(&self) -> &'a str360     fn get_name(&self) -> &'a str {
361         self.message.get_name()
362     }
363 }
364 
365 impl<'a> MessageWithScope<'a> {
into_scope(mut self) -> Scope<'a>366     pub fn into_scope(mut self) -> Scope<'a> {
367         self.scope.path.push(self.message);
368         self.scope
369     }
370 
to_scope(&self) -> Scope<'a>371     pub fn to_scope(&self) -> Scope<'a> {
372         self.clone().into_scope()
373     }
374 
fields(&self) -> Vec<FieldWithContext<'a>>375     pub fn fields(&self) -> Vec<FieldWithContext<'a>> {
376         self.message
377             .get_field()
378             .iter()
379             .map(|f| FieldWithContext {
380                 field: f,
381                 message: self.clone(),
382             })
383             .collect()
384     }
385 
oneofs(&self) -> Vec<OneofWithContext<'a>>386     pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> {
387         self.message
388             .get_oneof_decl()
389             .iter()
390             .enumerate()
391             .map(|(index, oneof)| OneofWithContext {
392                 message: self.clone(),
393                 oneof: &oneof,
394                 index: index as u32,
395             })
396             .collect()
397     }
398 
oneof_by_index(&self, index: u32) -> OneofWithContext<'a>399     pub fn oneof_by_index(&self, index: u32) -> OneofWithContext<'a> {
400         self.oneofs().swap_remove(index as usize)
401     }
402 
403     /// Pair of (key, value) if this message is map entry
map_entry(&'a self) -> Option<(FieldWithContext<'a>, FieldWithContext<'a>)>404     pub fn map_entry(&'a self) -> Option<(FieldWithContext<'a>, FieldWithContext<'a>)> {
405         if self.message.get_options().get_map_entry() {
406             let key = self
407                 .fields()
408                 .into_iter()
409                 .find(|f| f.field.get_number() == 1)
410                 .unwrap();
411             let value = self
412                 .fields()
413                 .into_iter()
414                 .find(|f| f.field.get_number() == 2)
415                 .unwrap();
416             Some((key, value))
417         } else {
418             None
419         }
420     }
421 }
422 
423 #[derive(Clone)]
424 pub struct EnumWithScope<'a> {
425     pub scope: Scope<'a>,
426     pub en: &'a EnumDescriptorProto,
427 }
428 
429 impl<'a> EnumWithScope<'a> {
430     // enum values
values(&'a self) -> &'a [EnumValueDescriptorProto]431     pub fn values(&'a self) -> &'a [EnumValueDescriptorProto] {
432         self.en.get_value()
433     }
434 
435     // find enum value by name
value_by_name(&'a self, name: &str) -> &'a EnumValueDescriptorProto436     pub fn value_by_name(&'a self, name: &str) -> &'a EnumValueDescriptorProto {
437         self.en
438             .get_value()
439             .into_iter()
440             .find(|v| v.get_name() == name)
441             .unwrap()
442     }
443 }
444 
445 pub trait EnumValueDescriptorEx {
rust_name(&self) -> String446     fn rust_name(&self) -> String;
447 }
448 
449 impl EnumValueDescriptorEx for EnumValueDescriptorProto {
rust_name(&self) -> String450     fn rust_name(&self) -> String {
451         let mut r = String::new();
452         if rust::is_rust_keyword(self.get_name()) {
453             r.push_str("value_");
454         }
455         r.push_str(self.get_name());
456         r
457     }
458 }
459 
460 impl<'a> WithScope<'a> for EnumWithScope<'a> {
get_scope(&self) -> &Scope<'a>461     fn get_scope(&self) -> &Scope<'a> {
462         &self.scope
463     }
464 
escape_prefix(&self) -> &'static str465     fn escape_prefix(&self) -> &'static str {
466         "enum_"
467     }
468 
get_name(&self) -> &'a str469     fn get_name(&self) -> &'a str {
470         self.en.get_name()
471     }
472 }
473 
474 pub enum MessageOrEnumWithScope<'a> {
475     Message(MessageWithScope<'a>),
476     Enum(EnumWithScope<'a>),
477 }
478 
479 impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> {
get_scope(&self) -> &Scope<'a>480     fn get_scope(&self) -> &Scope<'a> {
481         match self {
482             &MessageOrEnumWithScope::Message(ref m) => m.get_scope(),
483             &MessageOrEnumWithScope::Enum(ref e) => e.get_scope(),
484         }
485     }
486 
escape_prefix(&self) -> &'static str487     fn escape_prefix(&self) -> &'static str {
488         match self {
489             &MessageOrEnumWithScope::Message(ref m) => m.escape_prefix(),
490             &MessageOrEnumWithScope::Enum(ref e) => e.escape_prefix(),
491         }
492     }
493 
get_name(&self) -> &'a str494     fn get_name(&self) -> &'a str {
495         match self {
496             &MessageOrEnumWithScope::Message(ref m) => m.get_name(),
497             &MessageOrEnumWithScope::Enum(ref e) => e.get_name(),
498         }
499     }
500 }
501 
502 pub trait FieldDescriptorProtoExt {
rust_name(&self) -> String503     fn rust_name(&self) -> String;
504 }
505 
506 impl FieldDescriptorProtoExt for FieldDescriptorProto {
rust_name(&self) -> String507     fn rust_name(&self) -> String {
508         if rust::is_rust_keyword(self.get_name()) {
509             format!("field_{}", self.get_name())
510         } else {
511             self.get_name().to_string()
512         }
513     }
514 }
515 
516 #[derive(Clone)]
517 pub struct FieldWithContext<'a> {
518     pub field: &'a FieldDescriptorProto,
519     pub message: MessageWithScope<'a>,
520 }
521 
522 impl<'a> FieldWithContext<'a> {
523     #[doc(hidden)]
is_oneof(&self) -> bool524     pub fn is_oneof(&self) -> bool {
525         self.field.has_oneof_index()
526     }
527 
oneof(&self) -> Option<OneofWithContext<'a>>528     pub fn oneof(&self) -> Option<OneofWithContext<'a>> {
529         if self.is_oneof() {
530             Some(
531                 self.message
532                     .oneof_by_index(self.field.get_oneof_index() as u32),
533             )
534         } else {
535             None
536         }
537     }
538 
number(&self) -> u32539     pub fn number(&self) -> u32 {
540         self.field.get_number() as u32
541     }
542 
543     /// Shortcut
name(&self) -> &str544     pub fn name(&self) -> &str {
545         self.field.get_name()
546     }
547 
548     // field name in generated code
549     #[deprecated]
rust_name(&self) -> String550     pub fn rust_name(&self) -> String {
551         self.field.rust_name()
552     }
553 
554     // From field to file root
containing_messages(&self) -> Vec<&'a DescriptorProto>555     pub fn containing_messages(&self) -> Vec<&'a DescriptorProto> {
556         let mut r = Vec::new();
557         r.push(self.message.message);
558         r.extend(self.message.scope.path.iter().rev());
559         r
560     }
561 }
562 
563 #[derive(Clone)]
564 pub struct OneofVariantWithContext<'a> {
565     pub oneof: &'a OneofWithContext<'a>,
566     pub field: &'a FieldDescriptorProto,
567 }
568 
569 #[derive(Clone)]
570 pub struct OneofWithContext<'a> {
571     pub oneof: &'a OneofDescriptorProto,
572     pub index: u32,
573     pub message: MessageWithScope<'a>,
574 }
575 
576 impl<'a> OneofWithContext<'a> {
577     /// Oneof rust name
name(&'a self) -> &'a str578     pub fn name(&'a self) -> &'a str {
579         match self.oneof.get_name() {
580             "type" => "field_type",
581             "box" => "field_box",
582             x => x,
583         }
584     }
585 
586     /// rust type name of enum
rust_name(&self) -> String587     pub fn rust_name(&self) -> String {
588         format!(
589             "{}_oneof_{}",
590             self.message.rust_name(),
591             self.oneof.get_name()
592         )
593     }
594 
595     /// Oneof variants
variants(&'a self) -> Vec<OneofVariantWithContext<'a>>596     pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> {
597         self.message
598             .fields()
599             .iter()
600             .filter(|f| f.field.has_oneof_index() && f.field.get_oneof_index() == self.index as i32)
601             .map(|f| OneofVariantWithContext {
602                 oneof: self,
603                 field: &f.field,
604             })
605             .collect()
606     }
607 }
608 
609 /// Find message by rust type name
find_message_by_rust_name<'a>( fd: &'a FileDescriptorProto, rust_name: &str, ) -> MessageWithScope<'a>610 pub fn find_message_by_rust_name<'a>(
611     fd: &'a FileDescriptorProto,
612     rust_name: &str,
613 ) -> MessageWithScope<'a> {
614     FileScope {
615         file_descriptor: fd,
616     }
617     .find_messages()
618     .into_iter()
619     .find(|m| m.rust_name() == rust_name)
620     .unwrap()
621 }
622 
623 /// Find enum by rust type name
find_enum_by_rust_name<'a>( fd: &'a FileDescriptorProto, rust_name: &str, ) -> EnumWithScope<'a>624 pub fn find_enum_by_rust_name<'a>(
625     fd: &'a FileDescriptorProto,
626     rust_name: &str,
627 ) -> EnumWithScope<'a> {
628     FileScope {
629         file_descriptor: fd,
630     }
631     .find_enums()
632     .into_iter()
633     .find(|e| e.rust_name() == rust_name)
634     .unwrap()
635 }
636 
637 #[cfg(test)]
638 mod test {
639 
640     use super::proto_path_to_rust_mod;
641 
642     #[test]
test_mod_path_proto_ext()643     fn test_mod_path_proto_ext() {
644         assert_eq!("proto", proto_path_to_rust_mod("proto.proto"));
645     }
646 
647     #[test]
test_mod_path_unknown_ext()648     fn test_mod_path_unknown_ext() {
649         assert_eq!("proto_proto3", proto_path_to_rust_mod("proto.proto3"));
650     }
651 
652     #[test]
test_mod_path_empty_ext()653     fn test_mod_path_empty_ext() {
654         assert_eq!("proto", proto_path_to_rust_mod("proto"));
655     }
656 }
657