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