asn1rs_model/model/protobuf/
mod.rs

1use crate::model::rust::*;
2use crate::model::*;
3use std::convert::Infallible;
4
5const TUPLE_VARIABLE_NAME_REPLACEMENT: &str = "value";
6const DATAENUM_VARIABLE_NAME_REPLACEMENT: &str = "value";
7
8#[allow(clippy::module_name_repetitions)]
9#[derive(Debug, Clone, PartialOrd, PartialEq)]
10pub enum ProtobufType {
11    Bool,
12    #[allow(dead_code)]
13    SFixed32,
14    #[allow(dead_code)]
15    SFixed64,
16    UInt32,
17    UInt64,
18    SInt32,
19    SInt64,
20    String,
21    Bytes,
22    BitsReprByBytesAndBitsLen,
23    Repeated(Box<ProtobufType>),
24    OneOf(Vec<(String, ProtobufType)>),
25    /// Indicates a complex, custom type that is
26    /// not one of rusts known types
27    Complex(String),
28}
29
30impl ProtobufType {
31    pub fn from(rust: &RustType) -> ProtobufType {
32        Model::definition_type_to_protobuf_type(rust)
33    }
34
35    pub fn to_rust(&self) -> RustType {
36        #[allow(clippy::match_same_arms)] // to have the same order as the original enum
37        match self {
38            ProtobufType::Bool => RustType::Bool,
39            ProtobufType::SFixed32 => RustType::I32(Range::inclusive(0, i32::MAX)),
40            ProtobufType::SFixed64 => RustType::I64(Range::inclusive(0, i64::MAX)),
41            ProtobufType::UInt32 => RustType::U32(Range::inclusive(0, u32::MAX)),
42            ProtobufType::UInt64 => RustType::U64(Range::none()),
43            ProtobufType::SInt32 => RustType::I32(Range::inclusive(0, i32::MAX)),
44            ProtobufType::SInt64 => RustType::I64(Range::inclusive(0, i64::MAX)),
45            ProtobufType::String => RustType::String(Size::Any, Charset::Utf8),
46            ProtobufType::Bytes => RustType::VecU8(Size::Any),
47            ProtobufType::BitsReprByBytesAndBitsLen => RustType::BitVec(Size::Any),
48            ProtobufType::Repeated(inner) => {
49                RustType::Vec(Box::new(inner.to_rust()), Size::Any, EncodingOrdering::Keep)
50            }
51            ProtobufType::OneOf(_) => panic!("ProtobufType::OneOf cannot be mapped to a RustType"),
52            ProtobufType::Complex(name) => RustType::Complex(name.clone(), None),
53        }
54    }
55
56    pub fn is_primitive(&self) -> bool {
57        #[allow(clippy::match_same_arms)] // to have the same order as the original enum
58        match self {
59            ProtobufType::Bool => true,
60            ProtobufType::SFixed32 => true,
61            ProtobufType::SFixed64 => true,
62            ProtobufType::UInt32 => true,
63            ProtobufType::UInt64 => true,
64            ProtobufType::SInt32 => true,
65            ProtobufType::SInt64 => true,
66            ProtobufType::String => true,
67            ProtobufType::Bytes | ProtobufType::BitsReprByBytesAndBitsLen => true,
68            ProtobufType::OneOf(_) => false,
69            ProtobufType::Complex(_) => false,
70            ProtobufType::Repeated(_) => false,
71        }
72    }
73}
74
75impl ToString for ProtobufType {
76    fn to_string(&self) -> String {
77        match self {
78            ProtobufType::Bool => "bool",
79            ProtobufType::SFixed32 => "sfixed32",
80            ProtobufType::SFixed64 => "sfixed64",
81            ProtobufType::UInt32 => "uint32",
82            ProtobufType::UInt64 => "uint64",
83            ProtobufType::SInt32 => "sint32",
84            ProtobufType::SInt64 => "sint64",
85            ProtobufType::String => "string",
86            ProtobufType::Bytes => "bytes",
87            ProtobufType::BitsReprByBytesAndBitsLen => "bytes",
88            ProtobufType::OneOf(_) => "oneof",
89            ProtobufType::Complex(name) => return name.clone(),
90            ProtobufType::Repeated(name) => return format!("repeated {}", name.to_string()),
91        }
92        .into()
93    }
94}
95
96pub trait ToProtobufType {
97    fn to_protobuf(&self) -> ProtobufType;
98}
99
100impl ToProtobufType for RustType {
101    fn to_protobuf(&self) -> ProtobufType {
102        ProtobufType::from(self)
103    }
104}
105
106#[derive(Debug, Clone, PartialOrd, PartialEq)]
107pub enum Protobuf {
108    Message(Vec<(String, ProtobufType)>),
109    Enum(Vec<String>),
110}
111
112impl Target for Protobuf {
113    type DefinitionType = Self;
114    type ValueReferenceType = Infallible;
115}
116
117impl Model<Protobuf> {
118    pub fn convert_rust_to_protobuf(rust_model: &Model<Rust>) -> Model<Protobuf> {
119        let mut model = Model {
120            name: rust_model.name.clone(),
121            oid: rust_model.oid.clone(),
122            imports: rust_model.imports.clone(),
123            definitions: Vec::with_capacity(rust_model.definitions.len()),
124            value_references: Vec::default(),
125        };
126        for Definition(name, rust) in &rust_model.definitions {
127            let proto = Self::definition_to_protobuf(rust);
128            model
129                .definitions
130                .push(Definition(proto_definition_name(name), proto));
131        }
132        model
133    }
134
135    pub fn definition_to_protobuf(rust: &Rust) -> Protobuf {
136        match rust {
137            Rust::Struct {
138                fields,
139                tag: _,
140                extension_after: _,
141                ordering: _,
142            } => {
143                let mut proto_fields = Vec::with_capacity(fields.len());
144                for field in fields.iter() {
145                    proto_fields.push((
146                        proto_field_name(field.name()),
147                        Self::definition_type_to_protobuf_type(field.r#type()),
148                    ));
149                }
150
151                Protobuf::Message(proto_fields)
152            }
153            Rust::Enum(r_enum) => {
154                Protobuf::Enum(r_enum.variants().map(|v| proto_variant_name(v)).collect())
155            }
156            Rust::DataEnum(enumeration) => {
157                let mut proto_enum = Vec::with_capacity(enumeration.len());
158                for variant in enumeration.variants() {
159                    proto_enum.push((
160                        proto_field_name(variant.name()),
161                        Self::definition_type_to_protobuf_type(variant.r#type()),
162                    ))
163                }
164                Protobuf::Message(vec![(
165                    DATAENUM_VARIABLE_NAME_REPLACEMENT.into(),
166                    ProtobufType::OneOf(proto_enum),
167                )])
168            }
169            Rust::TupleStruct { r#type: inner, .. } => Protobuf::Message(vec![(
170                TUPLE_VARIABLE_NAME_REPLACEMENT.into(),
171                Self::definition_type_to_protobuf_type(inner),
172            )]),
173        }
174    }
175
176    pub fn definition_type_to_protobuf_type(rust_type: &RustType) -> ProtobufType {
177        #[allow(clippy::match_same_arms)] // to have the same order as the original enum
178        match rust_type {
179            RustType::Bool => ProtobufType::Bool,
180            RustType::U8(_) => ProtobufType::UInt32,
181            RustType::I8(_) => ProtobufType::SInt32,
182            RustType::U16(_) => ProtobufType::UInt32,
183            RustType::I16(_) => ProtobufType::SInt32,
184            RustType::U32(_) => ProtobufType::UInt32,
185            RustType::I32(_) => ProtobufType::SInt32,
186            RustType::U64(_) => ProtobufType::UInt64,
187            RustType::I64(_) => ProtobufType::SInt64,
188            RustType::String(..) => ProtobufType::String,
189            RustType::VecU8(_) => ProtobufType::Bytes,
190            RustType::BitVec(_) => ProtobufType::BitsReprByBytesAndBitsLen,
191            RustType::Null => ProtobufType::Bytes,
192
193            RustType::Complex(complex, _) => ProtobufType::Complex(complex.clone()),
194
195            RustType::Option(inner) => {
196                // in protobuf everything is optional...
197                Self::definition_type_to_protobuf_type(inner)
198            }
199            RustType::Default(inner, ..) => {
200                // TODO ignoring it in protobuf, is there a proper solution?
201                Self::definition_type_to_protobuf_type(inner)
202            }
203
204            RustType::Vec(inner, _size, _ordering) => {
205                ProtobufType::Repeated(Box::new(Self::definition_type_to_protobuf_type(inner)))
206            }
207        }
208    }
209}
210
211pub trait ToProtobufModel {
212    fn to_protobuf(&self) -> Model<Protobuf>;
213}
214
215impl ToProtobufModel for Model<Rust> {
216    fn to_protobuf(&self) -> Model<Protobuf> {
217        Model::convert_rust_to_protobuf(self)
218    }
219}
220
221pub fn proto_field_name(name: &str) -> String {
222    rust_module_name(name, false)
223}
224
225pub fn proto_variant_name(name: &str) -> String {
226    rust_variant_name(name)
227}
228
229pub fn proto_definition_name(name: &str) -> String {
230    rust_struct_or_enum_name(name)
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    #[test]
238    fn test_non_definitions_rust_to_protobuf() {
239        let mut model_rust = Model::default();
240        model_rust.name = "ModelWithOriginOfRust".into();
241        model_rust.imports = vec![Import {
242            what: vec!["a".into(), "b".into()],
243            from: "some_very_specific_module".into(),
244            from_oid: None,
245        }];
246        let model_proto = model_rust.to_protobuf();
247        assert_eq!(model_rust.name, model_proto.name);
248        assert_eq!(model_rust.imports, model_proto.imports);
249        assert!(model_proto.definitions.is_empty());
250    }
251
252    #[test]
253    fn test_simple_rust_struct_to_protobuf() {
254        test_model_definition_conversion(
255            &[Definition(
256                "Mine".into(),
257                Rust::struct_from_fields(vec![Field::from_name_type(
258                    "field",
259                    RustType::U8(Range::inclusive(0, 255)),
260                )]),
261            )],
262            &[Definition(
263                "Mine".into(),
264                Protobuf::Message(vec![("field".into(), ProtobufType::UInt32)]),
265            )],
266        );
267    }
268
269    #[test]
270    fn test_simple_rust_tuple_to_protobuf() {
271        test_model_definition_conversion(
272            &[Definition(
273                "SuchTuple".into(),
274                Rust::tuple_struct_from_type(RustType::Complex("VeryWow".into(), None)),
275            )],
276            &[Definition(
277                "SuchTuple".into(),
278                Protobuf::Message(vec![(
279                    TUPLE_VARIABLE_NAME_REPLACEMENT.into(),
280                    ProtobufType::Complex("VeryWow".into()),
281                )]),
282            )],
283        );
284    }
285
286    #[test]
287    fn test_simple_rust_enum_to_protobuf() {
288        test_model_definition_conversion(
289            &[Definition(
290                "SuchEnum".into(),
291                Rust::Enum(vec!["VeryWow".into(), "MuchGreat".into()].into()),
292            )],
293            &[Definition(
294                "SuchEnum".into(),
295                Protobuf::Enum(vec!["VeryWow".into(), "MuchGreat".into()]),
296            )],
297        );
298    }
299
300    #[test]
301    fn test_simple_rust_strucht_with_option_to_protobuf() {
302        test_model_definition_conversion(
303            &[Definition(
304                "SuchStruct".into(),
305                Rust::struct_from_fields(vec![Field::from_name_type(
306                    "very_optional",
307                    RustType::Option(Box::new(RustType::String(Size::Any, Charset::Utf8))),
308                )]),
309            )],
310            &[Definition(
311                "SuchStruct".into(),
312                Protobuf::Message(vec![("very_optional".into(), ProtobufType::String)]),
313            )],
314        );
315    }
316
317    #[test]
318    fn test_simple_rust_data_enum_to_protobuf() {
319        test_model_definition_conversion(
320            &[Definition(
321                "SuchDataEnum".into(),
322                Rust::DataEnum(
323                    vec![DataVariant::from_name_type(
324                        "MuchVariant",
325                        RustType::String(Size::Any, Charset::Utf8),
326                    )]
327                    .into(),
328                ),
329            )],
330            &[Definition(
331                "SuchDataEnum".into(),
332                Protobuf::Message(vec![(
333                    DATAENUM_VARIABLE_NAME_REPLACEMENT.into(),
334                    ProtobufType::OneOf(vec![("much_variant".into(), ProtobufType::String)]),
335                )]),
336            )],
337        );
338    }
339
340    #[test]
341    fn test_multiple_rust_defs_to_protobuf() {
342        test_model_definition_conversion(
343            &[
344                Definition(
345                    "First".into(),
346                    Rust::Enum(vec!["A".into(), "B".into()].into()),
347                ),
348                Definition(
349                    "Second".into(),
350                    Rust::tuple_struct_from_type(RustType::VecU8(Size::Any)),
351                ),
352            ],
353            &[
354                Definition("First".into(), Protobuf::Enum(vec!["A".into(), "B".into()])),
355                Definition(
356                    "Second".into(),
357                    Protobuf::Message(vec![(
358                        TUPLE_VARIABLE_NAME_REPLACEMENT.into(),
359                        ProtobufType::Bytes,
360                    )]),
361                ),
362            ],
363        )
364    }
365
366    fn test_model_definition_conversion(rust: &[Definition<Rust>], proto: &[Definition<Protobuf>]) {
367        let mut model_rust = Model::default();
368        model_rust.definitions = rust.to_vec();
369        let model_proto = model_rust.to_protobuf();
370        assert_eq!(proto.len(), model_proto.definitions.len());
371        assert_eq!(proto, &model_proto.definitions[..])
372    }
373}