• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::cmp;
2 
3 use super::well_known_types::is_well_known_type_full;
4 use inside::protobuf_crate_path;
5 use message::RustTypeMessage;
6 use protobuf::descriptor::*;
7 use protobuf_name::ProtobufAbsolutePath;
8 use rust_name::RustIdent;
9 use scope::RootScope;
10 use scope::WithScope;
11 use strx::capitalize;
12 use Customize;
13 
14 // Represent subset of rust types used in generated code
15 #[derive(Debug, Clone, PartialEq, Eq)]
16 pub(crate) enum RustType {
17     // integer: signed?, size in bits
18     Int(bool, u32),
19     // param is size in bits
20     Float(u32),
21     Bool,
22     Vec(Box<RustType>),
23     HashMap(Box<RustType>, Box<RustType>),
24     String,
25     // [T], not &[T]
26     Slice(Box<RustType>),
27     // str, not &str
28     Str,
29     Option(Box<RustType>),
30     SingularField(Box<RustType>),
31     SingularPtrField(Box<RustType>),
32     RepeatedField(Box<RustType>),
33     // Box<T>
34     Uniq(Box<RustType>),
35     // &T
36     Ref(Box<RustType>),
37     // protobuf message
38     Message(RustTypeMessage),
39     // protobuf enum, not any enum
40     Enum(String, RustIdent),
41     // oneof enum
42     Oneof(String),
43     // bytes::Bytes
44     Bytes,
45     // chars::Chars
46     Chars,
47     // group
48     Group,
49 }
50 
51 impl RustType {
52     #[inline]
to_code(&self, customize: &Customize) -> String53     pub(crate) fn to_code(&self, customize: &Customize) -> String {
54         match *self {
55             RustType::Int(true, bits) => format!("i{}", bits),
56             RustType::Int(false, bits) => format!("u{}", bits),
57             RustType::Float(bits) => format!("f{}", bits),
58             RustType::Bool => format!("bool"),
59             RustType::Vec(ref param) => format!("::std::vec::Vec<{}>", param.to_code(customize)),
60             RustType::HashMap(ref key, ref value) => format!(
61                 "::std::collections::HashMap<{}, {}>",
62                 key.to_code(customize),
63                 value.to_code(customize)
64             ),
65             RustType::String => format!("::std::string::String"),
66             RustType::Slice(ref param) => format!("[{}]", param.to_code(customize)),
67             RustType::Str => format!("str"),
68             RustType::Option(ref param) => {
69                 format!("::std::option::Option<{}>", param.to_code(customize))
70             }
71             RustType::SingularField(ref param) => format!(
72                 "{}::SingularField<{}>",
73                 protobuf_crate_path(customize),
74                 param.to_code(customize)
75             ),
76             RustType::SingularPtrField(ref param) => format!(
77                 "{}::SingularPtrField<{}>",
78                 protobuf_crate_path(customize),
79                 param.to_code(customize)
80             ),
81             RustType::RepeatedField(ref param) => format!(
82                 "{}::RepeatedField<{}>",
83                 protobuf_crate_path(customize),
84                 param.to_code(customize)
85             ),
86             RustType::Uniq(ref param) => format!("::std::boxed::Box<{}>", param.to_code(customize)),
87             RustType::Ref(ref param) => format!("&{}", param.to_code(customize)),
88             RustType::Message(ref name) => format!("{}", name),
89             RustType::Enum(ref name, _) | RustType::Oneof(ref name) => format!("{}", name),
90             RustType::Group => format!("<group>"),
91             RustType::Bytes => format!("::bytes::Bytes"),
92             RustType::Chars => format!("{}::Chars", protobuf_crate_path(customize)),
93         }
94     }
95 }
96 
97 impl RustType {
u8() -> RustType98     pub fn u8() -> RustType {
99         RustType::Int(false, 8)
100     }
101 
102     /// Type is rust primitive?
is_primitive(&self) -> bool103     pub fn is_primitive(&self) -> bool {
104         match *self {
105             RustType::Int(..) | RustType::Float(..) | RustType::Bool => true,
106             _ => false,
107         }
108     }
109 
is_u8(&self) -> bool110     pub fn is_u8(&self) -> bool {
111         match *self {
112             RustType::Int(false, 8) => true,
113             _ => false,
114         }
115     }
116 
is_copy(&self) -> bool117     pub fn is_copy(&self) -> bool {
118         if self.is_primitive() {
119             true
120         } else if let RustType::Enum(..) = *self {
121             true
122         } else {
123             false
124         }
125     }
126 
is_str(&self) -> bool127     fn is_str(&self) -> bool {
128         match *self {
129             RustType::Str => true,
130             _ => false,
131         }
132     }
133 
is_string(&self) -> bool134     fn is_string(&self) -> bool {
135         match *self {
136             RustType::String => true,
137             _ => false,
138         }
139     }
140 
is_slice(&self) -> Option<&RustType>141     fn is_slice(&self) -> Option<&RustType> {
142         match *self {
143             RustType::Slice(ref v) => Some(&**v),
144             _ => None,
145         }
146     }
147 
is_slice_u8(&self) -> bool148     fn is_slice_u8(&self) -> bool {
149         match self.is_slice() {
150             Some(t) => t.is_u8(),
151             None => false,
152         }
153     }
154 
is_message(&self) -> bool155     fn is_message(&self) -> bool {
156         match *self {
157             RustType::Message(..) => true,
158             _ => false,
159         }
160     }
161 
is_enum(&self) -> bool162     fn is_enum(&self) -> bool {
163         match *self {
164             RustType::Enum(..) => true,
165             _ => false,
166         }
167     }
168 
is_ref(&self) -> bool169     pub fn is_ref(&self) -> bool {
170         match *self {
171             RustType::Ref(..) => true,
172             _ => false,
173         }
174     }
175 
176     // default value for type
default_value(&self, customize: &Customize) -> String177     pub fn default_value(&self, customize: &Customize) -> String {
178         match *self {
179             RustType::Ref(ref t) if t.is_str() => "\"\"".to_string(),
180             RustType::Ref(ref t) if t.is_slice().is_some() => "&[]".to_string(),
181             RustType::Int(..) => "0".to_string(),
182             RustType::Float(..) => "0.".to_string(),
183             RustType::Bool => "false".to_string(),
184             RustType::Vec(..) => "::std::vec::Vec::new()".to_string(),
185             RustType::HashMap(..) => "::std::collections::HashMap::new()".to_string(),
186             RustType::String => "::std::string::String::new()".to_string(),
187             RustType::Bytes => "::bytes::Bytes::new()".to_string(),
188             RustType::Chars => format!("{}::Chars::new()", protobuf_crate_path(customize)),
189             RustType::Option(..) => "::std::option::Option::None".to_string(),
190             RustType::SingularField(..) => {
191                 format!("{}::SingularField::none()", protobuf_crate_path(customize))
192             }
193             RustType::SingularPtrField(..) => format!(
194                 "{}::SingularPtrField::none()",
195                 protobuf_crate_path(customize)
196             ),
197             RustType::RepeatedField(..) => {
198                 format!("{}::RepeatedField::new()", protobuf_crate_path(customize))
199             }
200             RustType::Message(ref name) => format!("{}::new()", name),
201             RustType::Ref(ref m) if m.is_message() => match **m {
202                 RustType::Message(ref name) => name.default_instance(customize),
203                 _ => unreachable!(),
204             },
205             // Note: default value of enum type may not be equal to default value of field
206             RustType::Enum(ref name, ref default) => format!("{}::{}", name, default),
207             _ => panic!("cannot create default value for: {:?}", *self),
208         }
209     }
210 
default_value_typed(self, customize: &Customize) -> RustValueTyped211     pub fn default_value_typed(self, customize: &Customize) -> RustValueTyped {
212         RustValueTyped {
213             value: self.default_value(customize),
214             rust_type: self,
215         }
216     }
217 
218     /// Emit a code to clear a variable `v`
clear(&self, v: &str, customize: &Customize) -> String219     pub fn clear(&self, v: &str, customize: &Customize) -> String {
220         match *self {
221             RustType::Option(..) => format!("{} = ::std::option::Option::None", v),
222             RustType::Vec(..)
223             | RustType::Bytes
224             | RustType::String
225             | RustType::RepeatedField(..)
226             | RustType::SingularField(..)
227             | RustType::SingularPtrField(..)
228             | RustType::HashMap(..) => format!("{}.clear()", v),
229             RustType::Chars => format!(
230                 "{}::Clear::clear(&mut {})",
231                 protobuf_crate_path(customize),
232                 v
233             ),
234             RustType::Bool | RustType::Float(..) | RustType::Int(..) | RustType::Enum(..) => {
235                 format!("{} = {}", v, self.default_value(customize))
236             }
237             ref ty => panic!("cannot clear type: {:?}", ty),
238         }
239     }
240 
241     // wrap value in storage type
wrap_value(&self, value: &str, customize: &Customize) -> String242     pub fn wrap_value(&self, value: &str, customize: &Customize) -> String {
243         match *self {
244             RustType::Option(..) => format!("::std::option::Option::Some({})", value),
245             RustType::SingularField(..) => format!(
246                 "{}::SingularField::some({})",
247                 protobuf_crate_path(customize),
248                 value
249             ),
250             RustType::SingularPtrField(..) => format!(
251                 "{}::SingularPtrField::some({})",
252                 protobuf_crate_path(customize),
253                 value
254             ),
255             _ => panic!("not a wrapper type: {:?}", *self),
256         }
257     }
258 
259     // expression to convert `v` of type `self` to type `target`
into_target(&self, target: &RustType, v: &str, customize: &Customize) -> String260     pub fn into_target(&self, target: &RustType, v: &str, customize: &Customize) -> String {
261         self.try_into_target(target, v, customize)
262             .expect(&format!("failed to convert {:?} into {:?}", self, target))
263     }
264 
try_into_target( &self, target: &RustType, v: &str, customize: &Customize, ) -> Result<String, ()>265     fn try_into_target(
266         &self,
267         target: &RustType,
268         v: &str,
269         customize: &Customize,
270     ) -> Result<String, ()> {
271         match (self, target) {
272             (x, y) if x == y => return Ok(format!("{}", v)),
273             (&RustType::Ref(ref x), y) if **x == *y => return Ok(format!("*{}", v)),
274             (x, &RustType::Uniq(ref y)) if *x == **y => {
275                 return Ok(format!("::std::boxed::Box::new({})", v))
276             }
277             (&RustType::Uniq(ref x), y) if **x == *y => return Ok(format!("*{}", v)),
278             (&RustType::String, &RustType::Ref(ref t)) if **t == RustType::Str => {
279                 return Ok(format!("&{}", v))
280             }
281             (&RustType::Chars, &RustType::Ref(ref t)) if **t == RustType::Str => {
282                 return Ok(format!("&{}", v))
283             }
284             (&RustType::Ref(ref t1), &RustType::Ref(ref t2)) if t1.is_string() && t2.is_str() => {
285                 return Ok(format!("&{}", v))
286             }
287             (&RustType::Ref(ref t1), &RustType::String)
288                 if match **t1 {
289                     RustType::Str => true,
290                     _ => false,
291                 } =>
292             {
293                 return Ok(format!("{}.to_owned()", v))
294             }
295             (&RustType::Ref(ref t1), &RustType::Chars)
296                 if match **t1 {
297                     RustType::Str => true,
298                     _ => false,
299                     // TODO: from_static
300                 } =>
301             {
302                 return Ok(format!(
303                     "<{}::Chars as ::std::convert::From<_>>::from({}.to_owned())",
304                     protobuf_crate_path(customize),
305                     v
306                 ))
307             }
308             (&RustType::Ref(ref t1), &RustType::Vec(ref t2))
309                 if match (&**t1, &**t2) {
310                     (&RustType::Slice(ref x), ref y) => **x == **y,
311                     _ => false,
312                 } =>
313             {
314                 return Ok(format!("{}.to_vec()", v))
315             }
316             (&RustType::Ref(ref t1), &RustType::Bytes) if t1.is_slice_u8() => {
317                 return Ok(format!(
318                     "<::bytes::Bytes as ::std::convert::From<_>>::from({}.to_vec())",
319                     v
320                 ))
321             }
322             (&RustType::Vec(ref x), &RustType::Ref(ref t))
323                 if match **t {
324                     RustType::Slice(ref y) => x == y,
325                     _ => false,
326                 } =>
327             {
328                 return Ok(format!("&{}", v))
329             }
330             (&RustType::Bytes, &RustType::Ref(ref t))
331                 if match **t {
332                     RustType::Slice(ref y) => **y == RustType::u8(),
333                     _ => false,
334                 } =>
335             {
336                 return Ok(format!("&{}", v))
337             }
338             (&RustType::Ref(ref t1), &RustType::Ref(ref t2))
339                 if match (&**t1, &**t2) {
340                     (&RustType::Vec(ref x), &RustType::Slice(ref y)) => x == y,
341                     _ => false,
342                 } =>
343             {
344                 return Ok(format!("&{}", v))
345             }
346             (&RustType::Enum(..), &RustType::Int(true, 32)) => {
347                 return Ok(format!(
348                     "{}::ProtobufEnum::value(&{})",
349                     protobuf_crate_path(customize),
350                     v
351                 ))
352             }
353             (&RustType::Ref(ref t), &RustType::Int(true, 32)) if t.is_enum() => {
354                 return Ok(format!(
355                     "{}::ProtobufEnum::value({})",
356                     protobuf_crate_path(customize),
357                     v
358                 ))
359             }
360             _ => (),
361         };
362 
363         if let &RustType::Ref(ref s) = self {
364             if let Ok(conv) = s.try_into_target(target, v, customize) {
365                 return Ok(conv);
366             }
367         }
368 
369         Err(())
370     }
371 
372     /// Type to view data of this type
ref_type(&self) -> RustType373     pub fn ref_type(&self) -> RustType {
374         RustType::Ref(Box::new(match self {
375             &RustType::String | &RustType::Chars => RustType::Str,
376             &RustType::Vec(ref p) | &RustType::RepeatedField(ref p) => RustType::Slice(p.clone()),
377             &RustType::Bytes => RustType::Slice(Box::new(RustType::u8())),
378             &RustType::Message(ref p) => RustType::Message(p.clone()),
379             x => panic!("no ref type for {:?}", x),
380         }))
381     }
382 
elem_type(&self) -> RustType383     pub fn elem_type(&self) -> RustType {
384         match self {
385             &RustType::Option(ref ty) => (**ty).clone(),
386             x => panic!("cannot get elem type of {:?}", x),
387         }
388     }
389 
390     // type of `v` in `for v in xxx`
iter_elem_type(&self) -> RustType391     pub fn iter_elem_type(&self) -> RustType {
392         match self {
393             &RustType::Vec(ref ty)
394             | &RustType::Option(ref ty)
395             | &RustType::RepeatedField(ref ty)
396             | &RustType::SingularField(ref ty)
397             | &RustType::SingularPtrField(ref ty) => RustType::Ref(ty.clone()),
398             x => panic!("cannot iterate {:?}", x),
399         }
400     }
401 
value(self, value: String) -> RustValueTyped402     pub fn value(self, value: String) -> RustValueTyped {
403         RustValueTyped {
404             value: value,
405             rust_type: self,
406         }
407     }
408 }
409 
410 /// Representation of an expression in code generator: text and type
411 pub(crate) struct RustValueTyped {
412     pub value: String,
413     pub rust_type: RustType,
414 }
415 
416 impl RustValueTyped {
into_type(&self, target: RustType, customize: &Customize) -> RustValueTyped417     pub fn into_type(&self, target: RustType, customize: &Customize) -> RustValueTyped {
418         let target_value = self.rust_type.into_target(&target, &self.value, customize);
419         RustValueTyped {
420             value: target_value,
421             rust_type: target,
422         }
423     }
424 
boxed(self, customize: &Customize) -> RustValueTyped425     pub fn boxed(self, customize: &Customize) -> RustValueTyped {
426         self.into_type(RustType::Uniq(Box::new(self.rust_type.clone())), customize)
427     }
428 }
429 
430 // protobuf type name for protobuf base type
protobuf_name(field_type: FieldDescriptorProto_Type) -> &'static str431 pub fn protobuf_name(field_type: FieldDescriptorProto_Type) -> &'static str {
432     match field_type {
433         FieldDescriptorProto_Type::TYPE_DOUBLE => "double",
434         FieldDescriptorProto_Type::TYPE_FLOAT => "float",
435         FieldDescriptorProto_Type::TYPE_INT32 => "int32",
436         FieldDescriptorProto_Type::TYPE_INT64 => "int64",
437         FieldDescriptorProto_Type::TYPE_UINT32 => "uint32",
438         FieldDescriptorProto_Type::TYPE_UINT64 => "uint64",
439         FieldDescriptorProto_Type::TYPE_SINT32 => "sint32",
440         FieldDescriptorProto_Type::TYPE_SINT64 => "sint64",
441         FieldDescriptorProto_Type::TYPE_FIXED32 => "fixed32",
442         FieldDescriptorProto_Type::TYPE_FIXED64 => "fixed64",
443         FieldDescriptorProto_Type::TYPE_SFIXED32 => "sfixed32",
444         FieldDescriptorProto_Type::TYPE_SFIXED64 => "sfixed64",
445         FieldDescriptorProto_Type::TYPE_BOOL => "bool",
446         FieldDescriptorProto_Type::TYPE_STRING => "string",
447         FieldDescriptorProto_Type::TYPE_BYTES => "bytes",
448         FieldDescriptorProto_Type::TYPE_ENUM => "enum",
449         FieldDescriptorProto_Type::TYPE_MESSAGE => "message",
450         FieldDescriptorProto_Type::TYPE_GROUP => "group",
451     }
452 }
453 
454 // rust type for protobuf base type
rust_name(field_type: FieldDescriptorProto_Type) -> RustType455 pub(crate) fn rust_name(field_type: FieldDescriptorProto_Type) -> RustType {
456     match field_type {
457         FieldDescriptorProto_Type::TYPE_DOUBLE => RustType::Float(64),
458         FieldDescriptorProto_Type::TYPE_FLOAT => RustType::Float(32),
459         FieldDescriptorProto_Type::TYPE_INT32 => RustType::Int(true, 32),
460         FieldDescriptorProto_Type::TYPE_INT64 => RustType::Int(true, 64),
461         FieldDescriptorProto_Type::TYPE_UINT32 => RustType::Int(false, 32),
462         FieldDescriptorProto_Type::TYPE_UINT64 => RustType::Int(false, 64),
463         FieldDescriptorProto_Type::TYPE_SINT32 => RustType::Int(true, 32),
464         FieldDescriptorProto_Type::TYPE_SINT64 => RustType::Int(true, 64),
465         FieldDescriptorProto_Type::TYPE_FIXED32 => RustType::Int(false, 32),
466         FieldDescriptorProto_Type::TYPE_FIXED64 => RustType::Int(false, 64),
467         FieldDescriptorProto_Type::TYPE_SFIXED32 => RustType::Int(true, 32),
468         FieldDescriptorProto_Type::TYPE_SFIXED64 => RustType::Int(true, 64),
469         FieldDescriptorProto_Type::TYPE_BOOL => RustType::Bool,
470         FieldDescriptorProto_Type::TYPE_STRING => RustType::String,
471         FieldDescriptorProto_Type::TYPE_BYTES => RustType::Vec(Box::new(RustType::Int(false, 8))),
472         FieldDescriptorProto_Type::TYPE_ENUM
473         | FieldDescriptorProto_Type::TYPE_GROUP
474         | FieldDescriptorProto_Type::TYPE_MESSAGE => {
475             panic!("there is no rust name for {:?}", field_type)
476         }
477     }
478 }
479 
file_last_component(file: &str) -> &str480 fn file_last_component(file: &str) -> &str {
481     let bs = file.rfind('\\').map(|i| i + 1).unwrap_or(0);
482     let fs = file.rfind('/').map(|i| i + 1).unwrap_or(0);
483     &file[cmp::max(fs, bs)..]
484 }
485 
486 #[cfg(test)]
487 #[test]
test_file_last_component()488 fn test_file_last_component() {
489     assert_eq!("ab.proto", file_last_component("ab.proto"));
490     assert_eq!("ab.proto", file_last_component("xx/ab.proto"));
491     assert_eq!("ab.proto", file_last_component("xx\\ab.proto"));
492     assert_eq!("ab.proto", file_last_component("yy\\xx\\ab.proto"));
493 }
494 
is_descriptor_proto(file: &FileDescriptorProto) -> bool495 fn is_descriptor_proto(file: &FileDescriptorProto) -> bool {
496     file.get_package() == "google.protobuf"
497         && file_last_component(file.get_name()) == "descriptor.proto"
498 }
499 
type_name_to_rust_relative( type_name: &ProtobufAbsolutePath, file: &FileDescriptorProto, subm: bool, root_scope: &RootScope, customize: &Customize, ) -> String500 pub(crate) fn type_name_to_rust_relative(
501     type_name: &ProtobufAbsolutePath,
502     file: &FileDescriptorProto,
503     subm: bool,
504     root_scope: &RootScope,
505     customize: &Customize,
506 ) -> String {
507     let message_or_enum = root_scope.find_message_or_enum(type_name);
508     if message_or_enum.get_scope().get_file_descriptor().get_name() == file.get_name() {
509         // field type is a message or enum declared in the same file
510         if subm {
511             format!("super::{}", message_or_enum.rust_name())
512         } else {
513             format!("{}", message_or_enum.rust_name())
514         }
515     } else if let Some(name) = is_well_known_type_full(&type_name.path) {
516         // Well-known types are included in rust-protobuf library
517         // https://developers.google.com/protocol-buffers/docs/reference/google.protobuf
518         format!(
519             "{}::well_known_types::{}",
520             protobuf_crate_path(customize),
521             name
522         )
523     } else if is_descriptor_proto(message_or_enum.get_file_descriptor()) {
524         // Messages defined in descriptor.proto
525         format!(
526             "{}::descriptor::{}",
527             protobuf_crate_path(customize),
528             message_or_enum.name_to_package()
529         )
530     } else {
531         if subm {
532             format!("super::super::{}", message_or_enum.rust_fq_name())
533         } else {
534             format!("super::{}", message_or_enum.rust_fq_name())
535         }
536     }
537 }
538 
539 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
540 pub enum PrimitiveTypeVariant {
541     Default,
542     Carllerche,
543 }
544 
545 pub enum _CarllercheBytesType {
546     Bytes,
547     Chars,
548 }
549 
550 // ProtobufType trait name
551 pub enum ProtobufTypeGen {
552     Primitive(FieldDescriptorProto_Type, PrimitiveTypeVariant),
553     Message(String),
554     Enum(String),
555 }
556 
557 impl ProtobufTypeGen {
rust_type(&self, customize: &Customize) -> String558     pub fn rust_type(&self, customize: &Customize) -> String {
559         match self {
560             &ProtobufTypeGen::Primitive(t, PrimitiveTypeVariant::Default) => format!(
561                 "{}::types::ProtobufType{}",
562                 protobuf_crate_path(customize),
563                 capitalize(protobuf_name(t))
564             ),
565             &ProtobufTypeGen::Primitive(
566                 FieldDescriptorProto_Type::TYPE_BYTES,
567                 PrimitiveTypeVariant::Carllerche,
568             ) => format!(
569                 "{}::types::ProtobufTypeCarllercheBytes",
570                 protobuf_crate_path(customize)
571             ),
572             &ProtobufTypeGen::Primitive(
573                 FieldDescriptorProto_Type::TYPE_STRING,
574                 PrimitiveTypeVariant::Carllerche,
575             ) => format!(
576                 "{}::types::ProtobufTypeCarllercheChars",
577                 protobuf_crate_path(customize)
578             ),
579             &ProtobufTypeGen::Primitive(.., PrimitiveTypeVariant::Carllerche) => unreachable!(),
580             &ProtobufTypeGen::Message(ref name) => format!(
581                 "{}::types::ProtobufTypeMessage<{}>",
582                 protobuf_crate_path(customize),
583                 name
584             ),
585             &ProtobufTypeGen::Enum(ref name) => format!(
586                 "{}::types::ProtobufTypeEnum<{}>",
587                 protobuf_crate_path(customize),
588                 name
589             ),
590         }
591     }
592 }
593