use crate::model::lor::{ResolveState, Resolved};
use crate::model::rust::Field as RustField;
use crate::model::{Asn, ChoiceVariant, Integer, LiteralValue, Target};
use crate::model::{Charset, Range};
use crate::model::{ComponentTypeList, ValueReference};
use crate::model::{Definition, Type};
use crate::model::{Import, Tag, TagProperty};
use crate::model::{Model, Size};
use crate::model::{TagResolver, Type as AsnType};
use std::borrow::Cow;
const I8_MAX: i64 = i8::max_value() as i64;
const I16_MAX: i64 = i16::max_value() as i64;
const I32_MAX: i64 = i32::max_value() as i64;
const U8_MAX: u64 = u8::max_value() as u64;
const U16_MAX: u64 = u16::max_value() as u64;
const U32_MAX: u64 = u32::max_value() as u64;
pub type PlainVariant = String;
pub type PlainEnum = Enumeration<PlainVariant>;
pub type DataEnum = Enumeration<DataVariant>;
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub enum RustType {
Bool,
I8(Range<i8>),
U8(Range<u8>),
I16(Range<i16>),
U16(Range<u16>),
I32(Range<i32>),
U32(Range<u32>),
I64(Range<i64>),
U64(Range<Option<u64>>),
String(Size, Charset),
VecU8(Size),
BitVec(Size),
Vec(Box<RustType>, Size, EncodingOrdering),
Null,
Option(Box<RustType>),
Default(Box<RustType>, LiteralValue),
Complex(String, Option<Tag>),
}
impl RustType {
pub fn as_inner_type(&self) -> &RustType {
if let RustType::Vec(inner, ..) | RustType::Option(inner) | RustType::Default(inner, ..) =
self
{
inner.as_inner_type()
} else {
self
}
}
pub fn into_inner_type(self) -> RustType {
if let RustType::Vec(inner, ..) | RustType::Option(inner) | RustType::Default(inner, ..) =
self
{
inner.into_inner_type()
} else {
self
}
}
pub fn to_inner_type_string(&self) -> String {
self.as_inner_type().to_string()
}
pub fn no_option(self) -> Self {
match self {
RustType::Option(inner) => *inner,
RustType::Default(inner, ..) => inner.no_option(),
rust => rust,
}
}
pub fn as_no_option(&self) -> &Self {
if let RustType::Option(inner) = self {
inner.as_no_option()
} else {
self
}
}
pub fn is_vec(&self) -> bool {
matches!(self.as_no_option(), RustType::Vec(..))
}
pub fn is_option(&self) -> bool {
matches!(self, RustType::Option(..))
}
pub fn is_optional(&self) -> bool {
matches!(self, RustType::Option(..) | RustType::Default(..))
}
pub fn is_primitive(&self) -> bool {
matches!(
self,
RustType::Bool
| RustType::U8(_)
| RustType::I8(_)
| RustType::U16(_)
| RustType::I16(_)
| RustType::U32(_)
| RustType::I32(_)
| RustType::U64(_)
| RustType::I64(_),
) || matches!(self, RustType::Default(inner, ..) if inner.is_primitive())
}
pub fn integer_range_str(&self) -> Option<Range<String>> {
#[allow(clippy::match_same_arms)] match self {
RustType::Bool => None,
RustType::U8(Range(min, max, extensible)) => {
Some(Range(min.to_string(), max.to_string(), *extensible))
}
RustType::I8(Range(min, max, extensible)) => {
Some(Range(min.to_string(), max.to_string(), *extensible))
}
RustType::U16(Range(min, max, extensible)) => {
Some(Range(min.to_string(), max.to_string(), *extensible))
}
RustType::I16(Range(min, max, extensible)) => {
Some(Range(min.to_string(), max.to_string(), *extensible))
}
RustType::U32(Range(min, max, extensible)) => {
Some(Range(min.to_string(), max.to_string(), *extensible))
}
RustType::I32(Range(min, max, extensible)) => {
Some(Range(min.to_string(), max.to_string(), *extensible))
}
RustType::U64(Range(min, max, extensible)) => Some(Range(
min.unwrap_or_default().to_string(),
max.unwrap_or_else(|| i64::max_value() as u64).to_string(),
*extensible,
)),
RustType::I64(Range(min, max, extensible)) => {
Some(Range(min.to_string(), max.to_string(), *extensible))
}
RustType::String(..) => None,
RustType::VecU8(_) => None,
RustType::BitVec(_) => None,
RustType::Vec(inner, _size, _ordering) => inner.integer_range_str(),
RustType::Null => None,
RustType::Option(inner) => inner.integer_range_str(),
RustType::Default(inner, ..) => inner.integer_range_str(),
RustType::Complex(_, _) => None,
}
}
pub fn into_asn(self) -> AsnType {
match self {
RustType::Bool => AsnType::Boolean,
RustType::I8(Range(min, max, extensible)) => AsnType::integer_with_range(Range(
Some(i64::from(min)),
Some(i64::from(max)),
extensible,
)),
RustType::U8(Range(min, max, extensible)) => AsnType::integer_with_range(Range(
Some(i64::from(min)),
Some(i64::from(max)),
extensible,
)),
RustType::I16(Range(min, max, extensible)) => AsnType::integer_with_range(Range(
Some(i64::from(min)),
Some(i64::from(max)),
extensible,
)),
RustType::U16(Range(min, max, extensible)) => AsnType::integer_with_range(Range(
Some(i64::from(min)),
Some(i64::from(max)),
extensible,
)),
RustType::I32(Range(min, max, extensible)) => AsnType::integer_with_range(Range(
Some(i64::from(min)),
Some(i64::from(max)),
extensible,
)),
RustType::U32(Range(min, max, extensible)) => AsnType::integer_with_range(Range(
Some(i64::from(min)),
Some(i64::from(max)),
extensible,
)),
RustType::I64(Range(min, max, extensible)) => {
AsnType::integer_with_range(Range(Some(min), Some(max), extensible))
}
RustType::U64(range) => AsnType::integer_with_range(Range(
range.min().map(|v| v as i64),
range.max().map(|v| v as i64),
range.extensible(),
)),
RustType::String(size, charset) => AsnType::String(size, charset),
RustType::VecU8(size) => AsnType::OctetString(size),
RustType::BitVec(size) => AsnType::bit_vec_with_size(size),
RustType::Vec(inner, size, EncodingOrdering::Keep) => {
AsnType::SequenceOf(Box::new(inner.into_asn()), size)
}
RustType::Vec(inner, size, EncodingOrdering::Sort) => {
AsnType::SetOf(Box::new(inner.into_asn()), size)
}
RustType::Null => AsnType::Null,
RustType::Option(value) => AsnType::Optional(Box::new(value.into_asn())),
RustType::Default(value, default) => {
AsnType::Default(Box::new(value.into_asn()), default)
}
RustType::Complex(name, tag) => AsnType::TypeReference(name, tag),
}
}
pub fn similar(&self, other: &Self) -> bool {
match self {
RustType::Bool => RustType::Bool == *other,
RustType::U8(_) => matches!(other, RustType::U8(_)),
RustType::I8(_) => matches!(other, RustType::I8(_)),
RustType::U16(_) => matches!(other, RustType::U16(_)),
RustType::I16(_) => matches!(other, RustType::I16(_)),
RustType::U32(_) => matches!(other, RustType::U32(_)),
RustType::I32(_) => matches!(other, RustType::I32(_)),
RustType::U64(_) => matches!(other, RustType::U64(_)),
RustType::I64(_) => matches!(other, RustType::I64(_)),
RustType::String(..) => matches!(other, RustType::String(..)),
RustType::VecU8(_) => matches!(other, RustType::VecU8(_)),
RustType::BitVec(_) => matches!(other, RustType::BitVec(_)),
RustType::Vec(inner_a, _size, _ordering) => {
if let RustType::Vec(inner_b, _other_size, _ordering) = other {
inner_a.similar(inner_b)
} else {
false
}
}
RustType::Null => RustType::Null == *other,
RustType::Option(inner) => {
matches!(other, RustType::Option(o) if o.similar(&*inner))
|| matches!(other, RustType::Default(o, ..) if o.similar(&*inner))
}
RustType::Default(inner, ..) => {
other.similar(&*inner)
|| matches!(other, RustType::Default(o, ..) if o.similar(&*inner))
|| matches!(other, RustType::Option(o, ..) if o.similar(&*inner))
}
RustType::Complex(inner_a, _tag) => {
if let RustType::Complex(inner_b, _tag) = other {
inner_a.eq(inner_b)
} else {
false
}
}
}
}
pub fn tag(&self) -> Option<Tag> {
Some(match self {
RustType::Bool => Tag::DEFAULT_BOOLEAN,
RustType::I8(_)
| RustType::U8(_)
| RustType::I16(_)
| RustType::U16(_)
| RustType::I32(_)
| RustType::U32(_)
| RustType::I64(_)
| RustType::U64(_) => Tag::DEFAULT_INTEGER,
RustType::BitVec(_) => Tag::DEFAULT_BIT_STRING,
RustType::VecU8(_) => Tag::DEFAULT_OCTET_STRING,
RustType::String(_, charset) => charset.default_tag(),
RustType::Vec(_, _, EncodingOrdering::Keep) => Tag::DEFAULT_SEQUENCE_OF,
RustType::Vec(_, _, EncodingOrdering::Sort) => Tag::DEFAULT_SET_OF,
RustType::Null => Tag::DEFAULT_NULL,
RustType::Option(inner) => return inner.tag(),
RustType::Default(inner, ..) => return inner.tag(),
RustType::Complex(_, tag) => return *tag,
})
}
}
#[derive(Debug, Clone, Copy, PartialOrd, PartialEq)]
pub enum EncodingOrdering {
Sort,
Keep,
}
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub enum Rust {
Struct {
ordering: EncodingOrdering,
fields: Vec<Field>,
tag: Option<Tag>,
extension_after: Option<usize>,
},
Enum(PlainEnum),
DataEnum(DataEnum),
TupleStruct {
r#type: RustType,
tag: Option<Tag>,
constants: Vec<(String, String)>,
},
}
impl Rust {
#[cfg(test)]
pub fn struct_from_fields(fields: Vec<Field>) -> Self {
Self::Struct {
ordering: EncodingOrdering::Keep,
fields,
tag: None,
extension_after: None,
}
}
pub fn tuple_struct_from_type(r#type: RustType) -> Self {
Self::TupleStruct {
r#type,
tag: None,
constants: Vec::default(),
}
}
}
impl Target for Rust {
type DefinitionType = Rust;
type ValueReferenceType = RustType;
}
impl TagProperty for Rust {
fn tag(&self) -> Option<Tag> {
match self {
Rust::Struct { tag, .. } => *tag,
Rust::Enum(e) => e.tag(),
Rust::DataEnum(c) => c.tag(),
Rust::TupleStruct { tag, .. } => *tag,
}
}
fn set_tag(&mut self, new_tag: Tag) {
match self {
Rust::Struct { tag, .. } => *tag = Some(new_tag),
Rust::Enum(e) => e.set_tag(new_tag),
Rust::DataEnum(c) => c.set_tag(new_tag),
Rust::TupleStruct { tag, .. } => *tag = Some(new_tag),
}
}
fn reset_tag(&mut self) {
match self {
Rust::Struct { tag, .. } => *tag = None,
Rust::Enum(e) => e.reset_tag(),
Rust::DataEnum(c) => c.reset_tag(),
Rust::TupleStruct { tag, .. } => *tag = None,
}
}
}
impl RustType {
pub fn to_const_lit_string(&self) -> Cow<'static, str> {
Cow::Borrowed(match self {
RustType::Bool => "bool",
RustType::U8(_) => "u8",
RustType::I8(_) => "i8",
RustType::U16(_) => "u16",
RustType::I16(_) => "i16",
RustType::U32(_) => "u32",
RustType::I32(_) => "i32",
RustType::U64(_) => "u64",
RustType::I64(_) => "i64",
RustType::String(..) => "&'static str",
RustType::VecU8(_) => "&'static [u8]",
RustType::BitVec(_) => "u64",
RustType::Vec(inner, _size, _ordering) => {
return Cow::Owned(format!("&'static [{}]", inner.to_const_lit_string()))
}
RustType::Null => "Null",
RustType::Option(inner) => {
return Cow::Owned(format!("Option<{}>", inner.to_const_lit_string()))
}
RustType::Default(inner, ..) => return inner.to_const_lit_string(),
RustType::Complex(name, _) => return Cow::Owned(name.clone()),
})
}
}
impl ToString for RustType {
fn to_string(&self) -> String {
match self {
RustType::Bool => "bool",
RustType::U8(_) => "u8",
RustType::I8(_) => "i8",
RustType::U16(_) => "u16",
RustType::I16(_) => "i16",
RustType::U32(_) => "u32",
RustType::I32(_) => "i32",
RustType::U64(_) => "u64",
RustType::I64(_) => "i64",
RustType::String(..) => "String",
RustType::VecU8(_) => "Vec<u8>",
RustType::BitVec(_) => "BitVec",
RustType::Vec(inner, _size, _ordering) => return format!("Vec<{}>", inner.to_string()),
RustType::Null => "Null",
RustType::Option(inner) => return format!("Option<{}>", inner.to_string()),
RustType::Default(inner, ..) => return inner.to_string(),
RustType::Complex(name, _) => return name.clone(),
}
.into()
}
}
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct Field {
pub(crate) name_type: (String, RustType),
pub(crate) tag: Option<Tag>,
pub(crate) constants: Vec<(String, String)>,
}
impl Field {
pub fn from_name_type<T: ToString>(name: T, r#type: RustType) -> Self {
Self {
name_type: (name.to_string(), r#type),
tag: None,
constants: Vec::default(),
}
}
pub fn fallback_representation(&self) -> &(String, RustType) {
&self.name_type
}
pub fn name(&self) -> &str {
&self.name_type.0
}
pub fn r#type(&self) -> &RustType {
&self.name_type.1
}
pub fn constants(&self) -> &[(String, String)] {
&self.constants[..]
}
pub fn with_constants(mut self, constants: Vec<(String, String)>) -> Self {
self.constants = constants;
self
}
}
impl TagProperty for Field {
fn tag(&self) -> Option<Tag> {
self.tag
}
fn set_tag(&mut self, tag: Tag) {
self.tag = Some(tag);
}
fn reset_tag(&mut self) {
self.tag = None;
}
}
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct Enumeration<T> {
variants: Vec<T>,
tag: Option<Tag>,
extended_after_index: Option<usize>,
}
impl<T> From<Vec<T>> for Enumeration<T> {
fn from(variants: Vec<T>) -> Self {
Enumeration {
variants,
tag: None,
extended_after_index: None,
}
}
}
impl<T> Enumeration<T> {
pub fn with_extension_after(mut self, extension_after: Option<usize>) -> Self {
self.extended_after_index = extension_after;
self
}
pub fn len(&self) -> usize {
self.variants.len()
}
pub fn is_empty(&self) -> bool {
self.variants.is_empty()
}
pub fn variants(&self) -> impl Iterator<Item = &T> {
self.variants.iter()
}
pub fn extension_after_index(&self) -> Option<usize> {
self.extended_after_index
}
pub fn extension_after_variant(&self) -> Option<&T> {
self.extended_after_index
.and_then(|index| self.variants.get(index))
}
pub fn is_extensible(&self) -> bool {
self.extended_after_index.is_some()
}
}
impl<T> TagProperty for Enumeration<T> {
fn tag(&self) -> Option<Tag> {
self.tag
}
fn set_tag(&mut self, tag: Tag) {
self.tag = Some(tag);
}
fn reset_tag(&mut self) {
self.tag = None;
}
}
impl PlainEnum {
pub fn from_names(names: impl Iterator<Item = impl ToString>) -> Self {
Self::from(names.map(|n| n.to_string()).collect::<Vec<_>>())
}
}
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct DataVariant {
name_type: (String, RustType),
tag: Option<Tag>,
}
impl DataVariant {
pub fn from_name_type<T: ToString>(name: T, r#type: RustType) -> Self {
Self {
name_type: (name.to_string(), r#type),
tag: None,
}
}
pub fn fallback_representation(&self) -> &(String, RustType) {
&self.name_type
}
pub fn name(&self) -> &str {
&self.name_type.0
}
pub fn r#type(&self) -> &RustType {
&self.name_type.1
}
}
impl TagProperty for DataVariant {
fn tag(&self) -> Option<Tag> {
self.tag
}
fn set_tag(&mut self, tag: Tag) {
self.tag = Some(tag);
}
fn reset_tag(&mut self) {
self.tag = None;
}
}
impl Model<Rust> {
pub fn convert_asn_to_rust(
asn_model: &Model<Asn>,
scope: &[&Model<Asn>],
make_names_nice: bool,
) -> Model<Rust> {
let mut definitions = Vec::with_capacity(asn_model.definitions.len());
let mut ctxt = Context {
resolver: TagResolver::new(asn_model, scope),
target: &mut definitions,
make_names_nice,
};
let mut model = Model {
name: ctxt.module_name(&asn_model.name),
oid: asn_model.oid.clone(),
imports: asn_model
.imports
.iter()
.map(|i| Import {
what: i.what.iter().map(|w| ctxt.struct_or_enum_name(w)).collect(),
from: ctxt.module_name(&i.from),
from_oid: i.from_oid.clone(),
})
.collect(),
definitions: Vec::default(),
value_references: Vec::with_capacity(asn_model.value_references.len()),
};
for Definition(name, asn) in &asn_model.definitions {
let rust_name = ctxt.struct_or_enum_name(name);
Self::definition_to_rust(&rust_name, &asn.r#type, asn.tag, &mut ctxt);
}
for vref in &asn_model.value_references {
if let Some(rust_type) = Self::map_asn_type_to_rust_type_flat(&vref.role.r#type) {
model.value_references.push(ValueReference {
name: ctxt.constant_name(&vref.name),
role: rust_type,
value: vref.value.clone(),
});
} else {
println!("Ignoring ValueReference {}", vref.name);
}
}
model.definitions = definitions;
model
}
fn map_asn_type_to_rust_type_flat(r#type: &Type) -> Option<RustType> {
Some(match &r#type {
Type::Boolean => RustType::Bool,
Type::Integer(int) if int.range.extensible() => {
Self::asn_extensible_integer_to_rust(int)
}
Type::Integer(int) => Self::asn_fixed_integer_to_rust_type(int),
Type::String(size, charset) => RustType::String(size.clone(), *charset),
Type::OctetString(size) => RustType::VecU8(size.clone()),
Type::BitString(bs) => RustType::BitVec(bs.size.clone()),
Type::Null => RustType::Null,
Type::Optional(opt) => {
RustType::Option(Box::new(Self::map_asn_type_to_rust_type_flat(&**opt)?))
}
Type::Default(inner, default) => RustType::Default(
Box::new(Self::map_asn_type_to_rust_type_flat(&**inner)?),
default.clone(),
),
Type::TypeReference(name, tag) => RustType::Complex(name.clone(), *tag),
Type::Sequence(_)
| Type::SequenceOf(_, _)
| Type::Set(_)
| Type::SetOf(_, _)
| Type::Enumerated(_)
| Type::Choice(_) => return None,
})
}
fn definition_to_rust(name: &str, asn: &AsnType, tag: Option<Tag>, ctxt: &mut Context<'_>) {
match asn {
AsnType::Boolean
| AsnType::Null
| AsnType::String(..)
| AsnType::OctetString(_)
| AsnType::BitString(_) => {
let rust_type = Self::definition_type_to_rust_type(name, asn, tag, ctxt);
ctxt.add_definition(Definition(
name.to_string(),
Rust::tuple_struct_from_type(rust_type).with_tag_opt(tag),
));
}
AsnType::TypeReference(_, tag) => {
let rust_type = Self::definition_type_to_rust_type(name, asn, *tag, ctxt);
ctxt.add_definition(Definition(
name.to_string(),
Rust::tuple_struct_from_type(rust_type).with_tag_opt(*tag),
));
}
me @ AsnType::Integer(_) => {
let rust_type = Self::definition_type_to_rust_type(name, asn, tag, ctxt);
let constants = ctxt.to_rust_constants(me);
ctxt.add_definition(Definition(
name.into(),
Rust::TupleStruct {
r#type: rust_type,
tag,
constants,
},
));
}
AsnType::Optional(inner) => {
let inner = RustType::Option(Box::new(Self::definition_type_to_rust_type(
name, inner, tag, ctxt,
)));
ctxt.add_definition(Definition(
name.into(),
Rust::tuple_struct_from_type(inner).with_tag_opt(tag),
))
}
AsnType::Default(inner, default) => {
let inner = RustType::Default(
Box::new(Self::definition_type_to_rust_type(name, inner, tag, ctxt)),
default.clone(),
);
ctxt.add_definition(Definition(
name.into(),
Rust::tuple_struct_from_type(inner).with_tag_opt(tag),
))
}
AsnType::Sequence(ComponentTypeList {
fields,
extension_after,
}) => {
let fields = Self::asn_fields_to_rust_fields(name, fields, ctxt);
ctxt.add_definition(Definition(
name.into(),
Rust::Struct {
ordering: EncodingOrdering::Keep,
fields,
tag,
extension_after: *extension_after,
},
));
}
AsnType::Set(ComponentTypeList {
fields,
extension_after,
}) => {
let fields = Self::asn_fields_to_rust_fields(name, fields, ctxt);
ctxt.add_definition(Definition(
name.into(),
Rust::Struct {
ordering: EncodingOrdering::Sort,
fields,
tag,
extension_after: *extension_after,
},
));
}
AsnType::SequenceOf(asn, size) => {
let inner = RustType::Vec(
Box::new(Self::definition_type_to_rust_type(name, asn, tag, ctxt)),
size.clone(),
EncodingOrdering::Keep,
);
ctxt.add_definition(Definition(name.into(), Rust::tuple_struct_from_type(inner)));
}
AsnType::SetOf(asn, size) => {
let inner = RustType::Vec(
Box::new(Self::definition_type_to_rust_type(name, asn, tag, ctxt)),
size.clone(),
EncodingOrdering::Sort,
);
ctxt.add_definition(Definition(
name.into(),
Rust::tuple_struct_from_type(inner).with_tag_opt(tag),
));
}
AsnType::Choice(choice) => {
let mut enumeration = Enumeration {
variants: Vec::with_capacity(choice.len()),
tag,
extended_after_index: choice.extension_after_index(),
};
for ChoiceVariant { name, r#type, tag } in choice.variants() {
let rust_name = format!("{}{}", name, ctxt.struct_or_enum_name(&name));
let rust_role =
Self::definition_type_to_rust_type(&rust_name, &r#type, *tag, ctxt);
let rust_field_name = ctxt.variant_name(&name);
enumeration.variants.push(
DataVariant::from_name_type(rust_field_name, rust_role).with_tag_opt(*tag),
);
}
ctxt.add_definition(Definition(name.into(), Rust::DataEnum(enumeration)));
}
AsnType::Enumerated(enumerated) => {
let mut rust_enum = Enumeration {
variants: Vec::with_capacity(enumerated.len()),
tag,
extended_after_index: enumerated.extension_after_index(),
};
for variant in enumerated.variants() {
rust_enum.variants.push(ctxt.variant_name(variant.name()));
}
ctxt.add_definition(Definition(name.into(), Rust::Enum(rust_enum)));
}
}
}
fn asn_fields_to_rust_fields(
name: &str,
fields: &[crate::model::Field<Asn>],
ctxt: &mut Context<'_>,
) -> Vec<Field> {
let mut rust_fields = Vec::with_capacity(fields.len());
for field in fields.iter() {
let rust_name = format!("{}{}", name, ctxt.struct_or_enum_name(&field.name));
let tag = field.role.tag;
let rust_role =
Self::definition_type_to_rust_type(&rust_name, &field.role.r#type, tag, ctxt);
let rust_role = if let Some(def) = &field.role.default {
RustType::Default(Box::new(rust_role.no_option()), def.clone())
} else {
rust_role
};
let rust_field_name = ctxt.field_name(&field.name);
let constants = ctxt.to_rust_constants(&field.role.r#type);
rust_fields.push(
RustField::from_name_type(rust_field_name, rust_role)
.with_constants(constants)
.with_tag_opt(tag),
);
}
rust_fields
}
fn definition_type_to_rust_type(
name: &str,
asn: &AsnType,
tag: Option<Tag>,
ctxt: &mut Context<'_>,
) -> RustType {
match asn {
AsnType::Boolean => RustType::Bool,
AsnType::Null => RustType::Null,
AsnType::Integer(int) if int.range.extensible() => {
Self::asn_extensible_integer_to_rust(int)
}
AsnType::Integer(int) => Self::asn_fixed_integer_to_rust_type(int),
AsnType::String(size, charset) => RustType::String(size.clone(), *charset),
AsnType::OctetString(size) => RustType::VecU8(size.clone()),
AsnType::BitString(bitstring) => RustType::BitVec(bitstring.size.clone()),
Type::Optional(inner) => {
RustType::Option(Box::new(Self::definition_type_to_rust_type(
name,
inner,
tag.or_else(|| ctxt.resolver().resolve_no_default(&**inner)),
ctxt,
)))
}
Type::Default(inner, default) => RustType::Default(
Box::new(Self::definition_type_to_rust_type(
name,
inner,
tag.or_else(|| ctxt.resolver().resolve_no_default(&**inner)),
ctxt,
)),
default.clone(),
),
AsnType::SequenceOf(asn, size) => RustType::Vec(
Box::new(Self::definition_type_to_rust_type(
name,
asn,
tag.or_else(|| ctxt.resolver().resolve_no_default(&**asn)),
ctxt,
)),
size.clone(),
EncodingOrdering::Keep,
),
AsnType::SetOf(asn, size) => RustType::Vec(
Box::new(Self::definition_type_to_rust_type(
name,
asn,
tag.or_else(|| ctxt.resolver().resolve_no_default(&**asn)),
ctxt,
)),
size.clone(),
EncodingOrdering::Sort,
),
ty @ AsnType::Sequence(_)
| ty @ AsnType::Set(_)
| ty @ AsnType::Enumerated(_)
| ty @ AsnType::Choice(_) => {
let name = ctxt.struct_or_enum_name(name);
Self::definition_to_rust(&name, asn, tag, ctxt);
RustType::Complex(name, tag.or_else(|| ctxt.resolver().resolve_type_tag(ty)))
}
AsnType::TypeReference(name, tag) => RustType::Complex(
ctxt.struct_or_enum_name(&name),
tag.clone().or_else(|| ctxt.resolver().resolve_tag(name)),
),
}
}
fn asn_extensible_integer_to_rust(
int: &Integer<<Resolved as ResolveState>::RangeType>,
) -> RustType {
match (int.range.min(), int.range.max()) {
(None, None) | (Some(0), None) | (Some(0), Some(i64::MAX)) | (None, Some(i64::MAX)) => {
RustType::U64(Range(None, None, true))
}
(min, max) if min.unwrap_or_default() >= 0 && max.unwrap_or_default() >= 0 => {
RustType::U64(Range(min.map(|v| v as u64), max.map(|v| v as u64), true))
}
(min, max) => RustType::I64(Range(
min.unwrap_or_else(i64::min_value),
max.unwrap_or_else(i64::max_value),
true,
)),
}
}
fn asn_fixed_integer_to_rust_type(
int: &Integer<<Resolved as ResolveState>::RangeType>,
) -> RustType {
match (int.range.min(), int.range.max()) {
(None, None) | (Some(0), None) | (Some(0), Some(i64::MAX)) | (None, Some(i64::MAX)) => {
RustType::U64(Range(None, None, false))
}
(min, max) => {
let min = min.unwrap_or_default();
let max = max.unwrap_or(i64::MAX);
if min >= 0 {
match max as u64 {
m if m <= U8_MAX => RustType::U8(Range::inclusive(min as u8, max as u8)),
m if m <= U16_MAX => RustType::U16(Range::inclusive(min as u16, max as u16)),
m if m <= U32_MAX => RustType::U32(Range::inclusive(min as u32, max as u32)),
_ => RustType::U64(Range::inclusive(Some(min as u64), Some(max as u64))),
}
} else {
let max_amplitude = (min + 1).abs().max(max);
match max_amplitude {
_ if max_amplitude <= I8_MAX => RustType::I8(Range::inclusive(min as i8, max as i8)),
_ if max_amplitude <= I16_MAX => RustType::I16(Range::inclusive(min as i16, max as i16)),
_ if max_amplitude <= I32_MAX => RustType::I32(Range::inclusive(min as i32, max as i32)),
_ => RustType::I64(Range::inclusive(min as i64, max as i64)),
}
}
}
}
}
}
struct Context<'a> {
resolver: TagResolver<'a>,
target: &'a mut Vec<Definition<Rust>>,
make_names_nice: bool,
}
impl Context<'_> {
fn to_rust_constants(&self, asn: &AsnType) -> Vec<(String, String)> {
match asn {
AsnType::Integer(integer) => integer
.constants
.iter()
.map(|(name, value)| (self.constant_name(name), format!("{}", value)))
.collect(),
AsnType::BitString(bitstring) => bitstring
.constants
.iter()
.map(|(name, value)| (self.constant_name(name), format!("{}", value)))
.collect(),
Type::Boolean
| Type::Null
| Type::String(..)
| Type::OctetString(_)
| Type::Optional(_)
| Type::Default(..)
| Type::Sequence(_)
| Type::SequenceOf(..)
| Type::Set(_)
| Type::SetOf(..)
| Type::Enumerated(_)
| Type::Choice(_)
| Type::TypeReference(_, _) => Vec::default(),
}
}
pub fn struct_or_enum_name(&self, name: &str) -> String {
if self.make_names_nice {
rust_struct_or_enum_name(name)
} else {
name.to_string()
}
}
pub fn constant_name(&self, name: &str) -> String {
if self.make_names_nice {
rust_constant_name(name)
} else {
name.to_string()
}
}
pub fn variant_name(&self, name: &str) -> String {
if self.make_names_nice {
rust_variant_name(name)
} else {
name.to_string()
}
}
pub fn field_name(&self, name: &str) -> String {
if self.make_names_nice {
rust_field_name(name)
} else {
name.to_string()
}
}
pub fn module_name(&self, name: &str) -> String {
if self.make_names_nice {
rust_module_name(name, false)
} else {
name.to_string()
}
}
pub fn add_definition(&mut self, def: Definition<Rust>) {
self.target.push(def)
}
pub fn resolver(&self) -> &TagResolver<'_> {
&self.resolver
}
}
#[allow(clippy::module_name_repetitions)]
pub fn rust_field_name(name: &str) -> String {
rust_module_name(name, false)
}
#[allow(clippy::module_name_repetitions)]
pub fn rust_variant_name(name: &str) -> String {
let mut out = String::new();
let mut next_upper = true;
let mut prev_upper = false;
let mut chars = name.chars().peekable();
while let Some(c) = chars.next() {
if c == '-' || c == '_' {
next_upper = true;
prev_upper = false;
} else if next_upper && !prev_upper {
out.push(c.to_ascii_uppercase());
next_upper = false;
prev_upper = true;
} else {
if prev_upper && !chars.peek().map(|c| c.is_lowercase()).unwrap_or(false) {
out.push(c.to_ascii_lowercase());
} else {
out.push(c);
}
prev_upper = c.is_ascii_uppercase();
}
}
out
}
#[allow(clippy::module_name_repetitions)]
pub fn rust_struct_or_enum_name(name: &str) -> String {
rust_variant_name(name)
}
#[allow(clippy::module_name_repetitions)]
pub fn rust_module_name(name: &str, pad_non_alphabetic: bool) -> String {
let mut out = String::new();
let mut prev_lowered = false;
let mut prev_alphabetic = false;
let mut chars = name.chars().peekable();
while let Some(c) = chars.next() {
let mut lowered = false;
let alphabetic = c.is_alphabetic();
if pad_non_alphabetic
&& prev_alphabetic != alphabetic
&& c != '-'
&& c != '_'
&& !out.is_empty()
&& !out.ends_with('_')
{
out.push('_');
}
if c.is_uppercase() {
if !out.is_empty() && prev_alphabetic {
if !prev_lowered {
out.push('_');
} else if let Some(next) = chars.peek() {
if next.is_lowercase() {
out.push('_');
}
}
}
lowered = true;
out.push_str(&c.to_lowercase().to_string());
} else if c == '-' || c == '_' {
out.push('_');
} else {
out.push(c);
}
prev_lowered = lowered;
prev_alphabetic = alphabetic;
}
out
}
#[allow(clippy::module_name_repetitions)]
pub fn rust_constant_name(name: &str) -> String {
rust_module_name(name, true).to_uppercase()
}
impl LiteralValue {
pub fn as_rust_const_literal_expect<F: FnOnce(&Self) -> bool>(
&self,
make_names_nice: bool,
probe: F,
) -> impl std::fmt::Display + '_ {
if probe(self) {
self.as_rust_const_literal(make_names_nice)
} else {
panic!("Invalid string literal {:?}", self)
}
}
pub fn as_rust_const_literal(&self, make_names_nice: bool) -> impl std::fmt::Display + '_ {
struct Ref<'a>(&'a LiteralValue, bool);
impl std::fmt::Display for Ref<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
LiteralValue::Boolean(v) => write!(f, "{}", v),
LiteralValue::String(v) => write!(f, "\"{}\"", v),
LiteralValue::Integer(v) => write!(f, "{}", v),
LiteralValue::OctetString(v) => {
write!(f, "[")?;
for b in v {
write!(f, "0x{:02x}, ", *b)?;
}
write!(f, "]")
}
LiteralValue::EnumeratedVariant(r#type, variant) => {
write!(
f,
"{}::{}",
if self.1 {
Cow::Owned(rust_struct_or_enum_name(r#type))
} else {
Cow::Borrowed(r#type)
},
if self.1 {
Cow::Owned(rust_variant_name(variant))
} else {
Cow::Borrowed(variant)
}
)
}
}
}
}
Ref(self, make_names_nice)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::gen::rust::walker::tests::assert_starts_with_lines;
use crate::gen::RustCodeGenerator;
use crate::model::tag::tests::test_property;
use crate::model::tests::*;
use crate::model::{Choice, Enumerated, EnumeratedVariant, Field, Tag, Type};
use crate::parser::Tokenizer;
#[test]
fn test_rust_struct_or_enum_name() {
fn stable_rust_struct_or_enum_name(name: &str) -> String {
let v1 = rust_struct_or_enum_name(name);
assert_eq!(v1, rust_struct_or_enum_name(&v1));
v1
}
assert_eq!("TestAbc", stable_rust_struct_or_enum_name("test-abc"));
assert_eq!(
"BerndDasBrot",
stable_rust_struct_or_enum_name("berndDasBrot")
);
assert_eq!(
"WhoKnowsWhat",
stable_rust_struct_or_enum_name("who-knowsWhat")
);
assert_eq!("EWaffle", stable_rust_struct_or_enum_name("e-waffle"));
assert_eq!("EeWaffle", stable_rust_struct_or_enum_name("ee-waffle"));
assert_eq!("EeWaffle", stable_rust_struct_or_enum_name("EEWaffle"));
}
#[test]
fn test_rust_variant_name() {
fn stable_rust_variant_name(name: &str) -> String {
let v1 = rust_variant_name(name);
assert_eq!(v1, rust_variant_name(&v1));
v1
}
assert_eq!("TestAbc", stable_rust_variant_name("test-abc"));
assert_eq!("BerndDasBrot", stable_rust_variant_name("berndDasBrot"));
assert_eq!("WhoKnowsWhat", stable_rust_variant_name("who-knowsWhat"));
assert_eq!("EWaffle", stable_rust_variant_name("e-waffle"));
assert_eq!("EeWaffle", stable_rust_variant_name("ee-waffle"));
assert_eq!("EeWaffle", stable_rust_variant_name("EEWaffle"));
}
#[test]
fn test_rust_constant_name() {
fn stable_constant_name(name: &str) -> String {
let v1 = rust_constant_name(name);
assert_eq!(v1, rust_constant_name(&v1));
v1
}
assert_eq!(
"SOME_IMPORTANT_VALUE_60_DEGREE_OFFSET_30_OTHER_10_MORE_42",
stable_constant_name("some-importantValue60degreeOffset-30-other10-more_42")
);
}
#[test]
fn test_rust_name_multiple_upper_case() {
assert_eq!(
"SomeThingyThingWithId",
rust_struct_or_enum_name("some-thingy-ThingWithID")
);
}
#[test]
fn test_simple_asn_sequence_represented_correctly_as_rust_model() {
let model_rust = Model::try_from(Tokenizer::default().parse(SIMPLE_INTEGER_STRUCT_ASN))
.unwrap()
.try_resolve()
.unwrap()
.to_rust();
assert_eq!("simple_schema", model_rust.name);
assert_eq!(true, model_rust.imports.is_empty());
assert_eq!(1, model_rust.definitions.len());
assert_eq!(
Definition(
"Simple".into(),
Rust::struct_from_fields(vec![
RustField::from_name_type("small", RustType::U8(Range::inclusive(0, 255))),
RustField::from_name_type("bigger", RustType::U16(Range::inclusive(0, 65535))),
RustField::from_name_type("negative", RustType::I16(Range::inclusive(-1, 255))),
RustField::from_name_type(
"unlimited",
RustType::Option(Box::new(RustType::U64(Range::none()))),
),
]),
),
model_rust.definitions[0]
);
}
#[test]
fn test_inline_asn_enumerated_represented_correctly_as_rust_model() {
let model_rust = Model::try_from(Tokenizer::default().parse(INLINE_ASN_WITH_ENUM))
.unwrap()
.try_resolve()
.unwrap()
.to_rust();
assert_eq!("simple_schema", model_rust.name);
assert_eq!(true, model_rust.imports.is_empty());
assert_eq!(2, model_rust.definitions.len());
assert_eq!(
Definition(
"WoahDecision".into(),
Rust::Enum(
vec![
"Abort".into(),
"Return".into(),
"Confirm".into(),
"Mayday".into(),
"TheCakeIsALie".into()
]
.into()
),
),
model_rust.definitions[0]
);
assert_eq!(
Definition(
"Woah".into(),
Rust::struct_from_fields(vec![RustField::from_name_type(
"decision",
RustType::Option(Box::new(RustType::Complex(
"WoahDecision".into(),
Some(Tag::DEFAULT_ENUMERATED)
))),
)])
),
model_rust.definitions[1]
);
}
#[test]
fn test_inline_asn_sequence_of_represented_correctly_as_rust_model() {
let model_rust = Model::try_from(Tokenizer::default().parse(INLINE_ASN_WITH_SEQUENCE_OF))
.unwrap()
.try_resolve()
.unwrap()
.to_rust();
assert_eq!("simple_schema", model_rust.name);
assert_eq!(true, model_rust.imports.is_empty());
assert_eq!(3, model_rust.definitions.len());
assert_eq!(
Definition(
"Ones".into(),
Rust::tuple_struct_from_type(RustType::Vec(
Box::new(RustType::U8(Range::inclusive(0, 1))),
Size::Any,
EncodingOrdering::Keep
)),
),
model_rust.definitions[0]
);
assert_eq!(
Definition(
"NestedOnes".into(),
Rust::tuple_struct_from_type(RustType::Vec(
Box::new(RustType::Vec(
Box::new(RustType::U8(Range::inclusive(0, 1))),
Size::Any,
EncodingOrdering::Keep
)),
Size::Any,
EncodingOrdering::Keep
)),
),
model_rust.definitions[1]
);
assert_eq!(
Definition(
"Woah".into(),
Rust::struct_from_fields(vec![
RustField::from_name_type(
"also_ones",
RustType::Vec(
Box::new(RustType::U8(Range::inclusive(0, 1))),
Size::Any,
EncodingOrdering::Keep
),
),
RustField::from_name_type(
"nesteds",
RustType::Vec(
Box::new(RustType::Vec(
Box::new(RustType::U8(Range::inclusive(0, 1))),
Size::Any,
EncodingOrdering::Keep
)),
Size::Any,
EncodingOrdering::Keep
),
),
RustField::from_name_type(
"optionals",
RustType::Option(Box::new(RustType::Vec(
Box::new(RustType::Vec(
Box::new(RustType::U64(Range::none())),
Size::Any,
EncodingOrdering::Keep
)),
Size::Any,
EncodingOrdering::Keep
))),
)
]),
),
model_rust.definitions[2]
);
}
#[test]
fn test_inline_asn_choice_represented_correctly_as_rust_model() {
let model_rust = Model::try_from(Tokenizer::default().parse(INLINE_ASN_WITH_CHOICE))
.unwrap()
.try_resolve()
.unwrap()
.to_rust();
assert_eq!("simple_schema", model_rust.name);
assert_eq!(true, model_rust.imports.is_empty());
assert_eq!(5, model_rust.definitions.len());
assert_eq!(
Definition(
"This".into(),
Rust::tuple_struct_from_type(RustType::Vec(
Box::new(RustType::U8(Range::inclusive(0, 1))),
Size::Any,
EncodingOrdering::Keep
)),
),
model_rust.definitions[0]
);
assert_eq!(
Definition(
"That".into(),
Rust::tuple_struct_from_type(RustType::Vec(
Box::new(RustType::Vec(
Box::new(RustType::U8(Range::inclusive(0, 1))),
Size::Any,
EncodingOrdering::Keep
)),
Size::Any,
EncodingOrdering::Keep
)),
),
model_rust.definitions[1]
);
assert_eq!(
Definition(
"Neither".into(),
Rust::Enum(vec!["Abc".into(), "Def".into(),].into()),
),
model_rust.definitions[2]
);
assert_eq!(
Definition(
"WoahDecision".into(),
Rust::DataEnum(
vec![
DataVariant::from_name_type(
"This",
RustType::Complex("This".into(), Some(Tag::DEFAULT_SEQUENCE_OF))
),
DataVariant::from_name_type(
"That",
RustType::Complex("That".into(), Some(Tag::DEFAULT_SEQUENCE_OF))
),
DataVariant::from_name_type(
"Neither",
RustType::Complex("Neither".into(), Some(Tag::DEFAULT_ENUMERATED))
),
]
.into()
)
),
model_rust.definitions[3]
);
assert_eq!(
Definition(
"Woah".into(),
Rust::struct_from_fields(vec![RustField::from_name_type(
"decision",
RustType::Complex("WoahDecision".into(), Some(Tag::DEFAULT_ENUMERATED)),
)])
),
model_rust.definitions[4]
);
}
#[test]
fn test_inline_asn_sequence_represented_correctly_as_rust_model() {
let model_rust = Model::try_from(Tokenizer::default().parse(INLINE_ASN_WITH_SEQUENCE))
.unwrap()
.try_resolve()
.unwrap()
.to_rust();
assert_eq!("simple_schema", model_rust.name);
assert_eq!(true, model_rust.imports.is_empty());
assert_eq!(2, model_rust.definitions.len());
assert_eq!(
Definition(
"WoahComplex".into(),
Rust::struct_from_fields(vec![
RustField::from_name_type("ones", RustType::U8(Range::inclusive(0, 1))),
RustField::from_name_type(
"list_ones",
RustType::Vec(
Box::new(RustType::U8(Range::inclusive(0, 1))),
Size::Any,
EncodingOrdering::Keep
),
),
RustField::from_name_type(
"optional_ones",
RustType::Option(Box::new(RustType::Vec(
Box::new(RustType::U8(Range::inclusive(0, 1,))),
Size::Any,
EncodingOrdering::Keep
))),
),
]),
),
model_rust.definitions[0]
);
assert_eq!(
Definition(
"Woah".into(),
Rust::struct_from_fields(vec![RustField::from_name_type(
"complex",
RustType::Option(Box::new(RustType::Complex(
"WoahComplex".into(),
Some(Tag::DEFAULT_SEQUENCE)
))),
)]),
),
model_rust.definitions[1]
);
}
#[test]
fn test_simple_enum() {
let mut model_asn = Model::default();
model_asn.definitions.push(Definition(
"SimpleEnumTest".into(),
AsnType::Enumerated(Enumerated::from_names(
["Bernd", "Das-Verdammte", "Brooot"].iter(),
))
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!(1, model_rust.definitions.len());
assert_eq!(
Definition(
"SimpleEnumTest".into(),
Rust::Enum(vec!["Bernd".into(), "DasVerdammte".into(), "Brooot".into(),].into()),
),
model_rust.definitions[0]
);
}
#[test]
fn test_choice_simple() {
let mut model_asn = Model::default();
model_asn.definitions.push(Definition(
"SimpleChoiceTest".into(),
AsnType::choice_from_variants(vec![
ChoiceVariant::name_type("bernd-das-brot", AsnType::unconstrained_utf8string()),
ChoiceVariant::name_type("nochSoEinBrot", AsnType::unconstrained_octetstring()),
])
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!(1, model_rust.definitions.len());
assert_eq!(
Definition(
"SimpleChoiceTest".into(),
Rust::DataEnum(
vec![
DataVariant::from_name_type(
"BerndDasBrot",
RustType::String(Size::Any, Charset::Utf8),
),
DataVariant::from_name_type("NochSoEinBrot", RustType::VecU8(Size::Any)),
]
.into()
),
),
model_rust.definitions[0]
)
}
#[test]
fn test_choice_list_and_nested_list() {
let mut model_asn = Model::default();
model_asn.definitions.push(Definition(
"ListChoiceTestWithNestedList".into(),
AsnType::choice_from_variants(vec![
ChoiceVariant::name_type(
"normal-List",
AsnType::SequenceOf(Box::new(AsnType::unconstrained_utf8string()), Size::Any),
),
ChoiceVariant::name_type(
"NESTED-List",
AsnType::SequenceOf(
Box::new(AsnType::SequenceOf(
Box::new(AsnType::unconstrained_octetstring()),
Size::Any,
)),
Size::Any,
),
),
])
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!(1, model_rust.definitions.len());
assert_eq!(
Definition(
"ListChoiceTestWithNestedList".into(),
Rust::DataEnum(
vec![
DataVariant::from_name_type(
"NormalList",
RustType::Vec(
Box::new(RustType::String(Size::Any, Charset::Utf8)),
Size::Any,
EncodingOrdering::Keep
),
),
DataVariant::from_name_type(
"NestedList",
RustType::Vec(
Box::new(RustType::Vec(
Box::new(RustType::VecU8(Size::Any)),
Size::Any,
EncodingOrdering::Keep
)),
Size::Any,
EncodingOrdering::Keep
),
),
]
.into()
),
),
model_rust.definitions[0]
)
}
#[test]
fn test_tuple_list() {
let mut model_asn = Model::default();
model_asn.name = "TupleTestModel".into();
model_asn.definitions.push(Definition(
"TupleTest".into(),
AsnType::SequenceOf(Box::new(AsnType::unconstrained_utf8string()), Size::Any)
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!("tuple_test_model", model_rust.name);
assert_eq!(model_asn.imports, model_rust.imports);
assert_eq!(1, model_rust.definitions.len());
assert_eq!(
Definition(
"TupleTest".into(),
Rust::tuple_struct_from_type(RustType::Vec(
Box::new(RustType::String(Size::Any, Charset::Utf8)),
Size::Any,
EncodingOrdering::Keep
)),
),
model_rust.definitions[0]
);
}
#[test]
fn test_nested_tuple_list() {
let mut model_asn = Model::default();
model_asn.name = "TupleTestModel".into();
model_asn.definitions.push(Definition(
"NestedTupleTest".into(),
AsnType::SequenceOf(
Box::new(AsnType::SequenceOf(
Box::new(AsnType::unconstrained_utf8string()),
Size::Any,
)),
Size::Any,
)
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!("tuple_test_model", model_rust.name);
assert_eq!(model_asn.imports, model_rust.imports);
assert_eq!(1, model_rust.definitions.len());
assert_eq!(
Definition(
"NestedTupleTest".into(),
Rust::tuple_struct_from_type(RustType::Vec(
Box::new(RustType::Vec(
Box::new(RustType::String(Size::Any, Charset::Utf8)),
Size::Any,
EncodingOrdering::Keep
)),
Size::Any,
EncodingOrdering::Keep
)),
),
model_rust.definitions[0]
);
}
#[test]
fn test_optional_list_in_struct() {
let mut model_asn = Model::default();
model_asn.name = "OptionalStructListTestModel".into();
model_asn.definitions.push(Definition(
"OptionalStructListTest".into(),
AsnType::sequence_from_fields(vec![Field {
name: "strings".into(),
role: AsnType::SequenceOf(Box::new(AsnType::unconstrained_utf8string()), Size::Any)
.optional()
.untagged(),
}])
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!("optional_struct_list_test_model", model_rust.name);
assert_eq!(model_asn.imports, model_rust.imports);
assert_eq!(1, model_rust.definitions.len());
assert_eq!(
Definition(
"OptionalStructListTest".into(),
Rust::struct_from_fields(vec![RustField::from_name_type(
"strings",
RustType::Option(Box::new(RustType::Vec(
Box::new(RustType::String(Size::Any, Charset::Utf8)),
Size::Any,
EncodingOrdering::Keep
))),
)]),
),
model_rust.definitions[0]
);
}
#[test]
fn test_list_in_struct() {
let mut model_asn = Model::default();
model_asn.name = "StructListTestModel".into();
model_asn.definitions.push(Definition(
"StructListTest".into(),
AsnType::sequence_from_fields(vec![Field {
name: "strings".into(),
role: AsnType::SequenceOf(Box::new(AsnType::unconstrained_utf8string()), Size::Any)
.untagged(),
}])
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!("struct_list_test_model", model_rust.name);
assert_eq!(model_asn.imports, model_rust.imports);
assert_eq!(1, model_rust.definitions.len());
assert_eq!(
Definition(
"StructListTest".into(),
Rust::struct_from_fields(vec![RustField::from_name_type(
"strings",
RustType::Vec(
Box::new(RustType::String(Size::Any, Charset::Utf8)),
Size::Any,
EncodingOrdering::Keep
),
)]),
),
model_rust.definitions[0]
);
}
#[test]
fn test_nested_list_in_struct() {
let mut model_asn = Model::default();
model_asn.name = "NestedStructListTestModel".into();
model_asn.definitions.push(Definition(
"NestedStructListTest".into(),
AsnType::sequence_from_fields(vec![Field {
name: "strings".into(),
role: AsnType::SequenceOf(
Box::new(AsnType::SequenceOf(
Box::new(AsnType::unconstrained_utf8string()),
Size::Any,
)),
Size::Any,
)
.untagged(),
}])
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!("nested_struct_list_test_model", model_rust.name);
assert_eq!(model_asn.imports, model_rust.imports);
assert_eq!(1, model_rust.definitions.len());
assert_eq!(
Definition(
"NestedStructListTest".into(),
Rust::struct_from_fields(vec![RustField::from_name_type(
"strings",
RustType::Vec(
Box::new(RustType::Vec(
Box::new(RustType::String(Size::Any, Charset::Utf8)),
Size::Any,
EncodingOrdering::Keep
)),
Size::Any,
EncodingOrdering::Keep
),
)]),
),
model_rust.definitions[0]
);
}
#[test]
pub fn test_extensible_enum() {
let mut model_asn = Model::default();
model_asn.name = "ExtensibleEnum".to_string();
model_asn.definitions.push(Definition(
"Extensible".to_string(),
AsnType::Enumerated(
Enumerated::from(vec![
"abc".into(),
"def".into(),
EnumeratedVariant::from_name_number("ghi", 42),
])
.with_extension_after(2),
)
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!("extensible_enum", model_rust.name);
assert_eq!(model_asn.imports, model_rust.imports);
assert_eq!(
&[Definition(
"Extensible".into(),
Rust::Enum(
PlainEnum::from_names(["Abc", "Def", "Ghi"].iter())
.with_extension_after(Some(2))
),
)],
&model_rust.definitions[..]
);
}
#[test]
pub fn test_extensible_choice() {
let mut model_asn = Model::default();
model_asn.name = "ExtensibleChoice".to_string();
model_asn.definitions.push(Definition(
"Extensible".to_string(),
AsnType::Choice(
Choice::from(vec![
ChoiceVariant::name_type("abc", Type::unconstrained_octetstring()),
ChoiceVariant::name_type("def", Type::unconstrained_integer()),
ChoiceVariant {
name: "ghi".to_string(),
tag: Some(Tag::Universal(4)),
r#type: Type::Boolean,
},
])
.with_extension_after(2),
)
.untagged(),
));
let model_rust = model_asn.to_rust();
assert_eq!("extensible_choice", model_rust.name);
assert_eq!(model_asn.imports, model_rust.imports);
assert_eq!(
&[Definition(
"Extensible".into(),
Rust::DataEnum(
DataEnum::from(vec![
DataVariant::from_name_type("Abc".to_string(), RustType::VecU8(Size::Any)),
DataVariant::from_name_type(
"Def".to_string(),
RustType::U64(Range::none()),
),
DataVariant::from_name_type("Ghi".to_string(), RustType::Bool)
.with_tag(Tag::Universal(4)),
])
.with_extension_after(Some(2))
),
)],
&model_rust.definitions[..]
);
}
#[test]
pub fn test_tag_property_rust_struct() {
test_property(Rust::Struct {
ordering: EncodingOrdering::Keep,
fields: Vec::default(),
tag: None,
extension_after: None,
});
}
#[test]
pub fn test_tag_property_rust_enum() {
test_property(Rust::Enum(PlainEnum::from_names(
Some("Variant").into_iter(),
)));
}
#[test]
pub fn test_tag_property_rust_data_enum() {
test_property(Rust::DataEnum(DataEnum::from(vec![
DataVariant::from_name_type(
"SomeName".to_string(),
RustType::String(Size::Any, Charset::Visible),
),
])));
}
#[test]
pub fn test_tag_property_rust_tuple_struct() {
test_property(Rust::TupleStruct {
r#type: RustType::VecU8(Size::Any),
tag: None,
constants: Vec::default(),
});
}
#[test]
pub fn test_tag_property_field() {
test_property(RustField::from_name_type(
"FieldName".to_string(),
RustType::Bool,
));
}
#[test]
pub fn test_tag_property_enumeration() {
test_property(Enumeration::from(vec!["VariantA", "VariantB"]));
}
#[test]
pub fn test_tag_property_data_variant() {
test_property(DataVariant::from_name_type(
"VariantName".to_string(),
RustType::Bool,
));
}
#[test]
pub fn test_value_reference_to_rust() {
let asn = Model::<Asn<Resolved>> {
name: "SomeGreatName".to_string(),
oid: None,
imports: Vec::default(),
definitions: Vec::default(),
value_references: vec![
ValueReference {
name: "local-http".to_string(),
role: AsnType::Integer(Integer::with_range(Range::inclusive(
None,
Some(65535),
)))
.untagged(),
value: LiteralValue::Integer(8080),
},
ValueReference {
name: "use-firewall".to_string(),
role: AsnType::Boolean.untagged(),
value: LiteralValue::Boolean(true),
},
],
};
assert_starts_with_lines(
r#"
use asn1rs::prelude::*;
pub const LOCAL_HTTP: u16 = 8080;
pub const USE_FIREWALL: bool = true;
"#,
&RustCodeGenerator::from(asn.to_rust())
.to_string_without_generators()
.into_iter()
.map(|(_f, c)| c)
.next()
.unwrap(),
);
}
#[test]
fn test_to_rust_coherent_complex_reference_renaming() {
let asn = Model::<Asn<Resolved>> {
name: "CoherentComplexRenaming".to_string(),
oid: None,
imports: vec![],
definitions: vec![
Definition("Some-Name-WithID".to_string(), Type::Boolean.untagged()),
Definition(
"Complex-Container".to_string(),
Type::Sequence(ComponentTypeList {
fields: vec![
Field {
name: "some-internal".to_string(),
role: Type::Boolean.untagged(),
},
Field {
name: "id".to_string(),
role: Type::TypeReference("Some-Name-WithID".to_string(), None)
.untagged(),
},
],
extension_after: None,
})
.untagged(),
),
],
value_references: vec![],
};
assert_eq!(
vec![
Definition(
"SomeNameWithId".to_string(),
Rust::TupleStruct {
r#type: RustType::Bool,
tag: None,
constants: vec![]
}
),
Definition(
"ComplexContainer".to_string(),
Rust::Struct {
ordering: EncodingOrdering::Keep,
fields: vec![
crate::model::rust::Field::from_name_type(
"some_internal".to_string(),
RustType::Bool
),
crate::model::rust::Field::from_name_type(
"id".to_string(),
RustType::Complex(
"SomeNameWithId".to_string(),
Some(Tag::Universal(1)) )
),
],
tag: None,
extension_after: None
}
),
],
asn.to_rust().definitions
);
}
}