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
12 use crate::rust;
13 use crate::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