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