nrc_protobuf_codegen/
rust_types_values.rs

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