Skip to content

Commit

Permalink
Properly resolve and set default value for an ENUMRATED type #56
Browse files Browse the repository at this point in the history
  • Loading branch information
kellerkindt committed Apr 12, 2021
1 parent 4a03f2f commit 0c1cd81
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 44 deletions.
66 changes: 34 additions & 32 deletions asn1rs-model/src/ast/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::model::LiteralValue;
use crate::model::{
Charset, Choice, ChoiceVariant, Enumerated, EnumeratedVariant, Range, Size, Tag, Type,
};
use quote::ToTokens;
use std::fmt::Debug;
use std::fmt::Display;
use std::marker::PhantomData;
Expand Down Expand Up @@ -180,40 +179,43 @@ fn parse_type_pre_stepped<'a>(
})?;

content.parse::<Token![,]>()?;
let span = content.span();
let ctnt = content.to_string();
let mut default = String::default();
if content.peek(Token![-]) {
content.parse::<Token![-]>()?;
default.push('-');
}

let ident = content
.parse::<syn::Ident>()
.map(|i| i.to_string())
.or_else(|_| {
content
.parse::<syn::Lit>()
.map(|l| l.to_token_stream().to_string())
})
.map_err(|e| {
syn::Error::new(
span,
format!(
"Failed to parse default value literal({}): {}",
ctnt,
e.to_string()
),
)
})?;

Ok(Type::Default(
Box::new(inner),
LiteralValue::try_from_asn_str(&{
default.push_str(&ident);
default
})
.ok_or_else(|| syn::Error::new(span, "Invalid literal value"))?,
content
.parse::<syn::Lit>()
.ok()
.and_then(|lit| {
Some(match lit {
syn::Lit::Str(val) => LiteralValue::String(val.value()),
syn::Lit::ByteStr(val) => LiteralValue::OctetString(val.value()),
syn::Lit::Byte(val) => LiteralValue::Integer(i64::from(val.value())),
syn::Lit::Int(val) => LiteralValue::Integer(val.base10_parse().ok()?),
syn::Lit::Bool(val) => LiteralValue::Boolean(val.value()),
syn::Lit::Char(_) | syn::Lit::Float(_) | syn::Lit::Verbatim(_) => {
return None
}
})
})
.or_else(|| {
content.parse::<syn::Path>().ok().and_then(|path| {
if path.segments.len() == 2 {
let mut iter = path.segments.iter();
Some(LiteralValue::EnumeratedVariant(
iter.next().unwrap().ident.to_string(),
iter.next().unwrap().ident.to_string(),
))
} else {
None
}
})
})
.ok_or_else(|| {
syn::Error::new(
span,
format!("Invalid literal value: {}", content.to_string()),
)
})?,
))
}
"boolean" => Ok(Type::Boolean),
Expand Down
4 changes: 3 additions & 1 deletion asn1rs-model/src/gen/rust/walker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,9 @@ impl AsnDefWriter {
let (owned, borrowed, default) = match r#type.as_no_option() {
RustType::Option(_) => unreachable!(),
RustType::Default(..) => panic!("Nested default detected"),
RustType::Complex(name, _tag) => {
RustType::Complex(name, _tag)
if !matches!(default, LiteralValue::EnumeratedVariant(..)) =>
{
//panic!("Complex default types unsupported")
(
Cow::<'_, str>::Borrowed(&name),
Expand Down
39 changes: 34 additions & 5 deletions asn1rs-model/src/model/asn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::model::lor::{Error as ResolveError, TryResolve, Unresolved};
use crate::model::lor::{ResolveState, Resolved, Resolver};
use crate::model::{
BitString, Charset, Choice, ChoiceVariant, ComponentTypeList, Enumerated, Field, Integer,
LiteralValue, Range, Size, Tag, TagProperty, Target,
LitOrRef, LiteralValue, Range, Size, Tag, TagProperty, Target,
};
use std::fmt::Debug;

Expand Down Expand Up @@ -69,19 +69,47 @@ impl Asn<Unresolved> {
pub fn try_resolve<
R: Resolver<<Resolved as ResolveState>::SizeType>
+ Resolver<<Resolved as ResolveState>::RangeType>
+ Resolver<<Resolved as ResolveState>::ConstType>,
+ Resolver<<Resolved as ResolveState>::ConstType>
+ Resolver<Type<Unresolved>>,
>(
&self,
resolver: &R,
) -> Result<Asn<Resolved>, ResolveError> {
let r#type = self.r#type.try_resolve(resolver)?;
Ok(Asn {
tag: self.tag,
r#type: self.r#type.try_resolve(resolver)?,
default: self
.default
.as_ref()
.map(|d| resolver.resolve(d))
.map(|d| match d {
LitOrRef::Lit(_) => resolver.resolve(d),
LitOrRef::Ref(name) => {
if let Type::TypeReference(referenced_name, _tag) = &r#type {
if let Ok(Type::Enumerated(enumerated)) =
resolver.resolve(&LitOrRef::Ref(referenced_name.to_string()))
{
if let Some(lit) =
enumerated.variants().find(|v| name.eq(v.name())).map(|v| {
LiteralValue::EnumeratedVariant(
referenced_name.to_string(),
v.name().to_string(),
)
})
{
Ok(lit)
} else {
resolver.resolve(d)
}
} else {
resolver.resolve(d)
}
} else {
resolver.resolve(d)
}
}
})
.transpose()?,
r#type,
})
}
}
Expand Down Expand Up @@ -194,7 +222,8 @@ impl Type<Unresolved> {
pub fn try_resolve<
R: Resolver<<Resolved as ResolveState>::SizeType>
+ Resolver<<Resolved as ResolveState>::RangeType>
+ Resolver<<Resolved as ResolveState>::ConstType>,
+ Resolver<<Resolved as ResolveState>::ConstType>
+ Resolver<Type<Unresolved>>,
>(
&self,
resolver: &R,
Expand Down
6 changes: 4 additions & 2 deletions asn1rs-model/src/model/choice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ impl Choice<Unresolved> {
pub fn try_resolve<
R: Resolver<<Resolved as ResolveState>::SizeType>
+ Resolver<<Resolved as ResolveState>::RangeType>
+ Resolver<<Resolved as ResolveState>::ConstType>,
+ Resolver<<Resolved as ResolveState>::ConstType>
+ Resolver<Type<Unresolved>>,
>(
&self,
resolver: &R,
Expand Down Expand Up @@ -161,7 +162,8 @@ impl ChoiceVariant<Unresolved> {
pub fn try_resolve<
R: Resolver<<Resolved as ResolveState>::SizeType>
+ Resolver<<Resolved as ResolveState>::RangeType>
+ Resolver<<Resolved as ResolveState>::ConstType>,
+ Resolver<<Resolved as ResolveState>::ConstType>
+ Resolver<Type<Unresolved>>,
>(
&self,
resolver: &R,
Expand Down
5 changes: 3 additions & 2 deletions asn1rs-model/src/model/components.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::model::lor::{Error as ResolveError, Resolved, Resolver};
use crate::model::lor::{ResolveState, Unresolved};
use crate::model::{Asn, Error, Field, Model, PeekableTokens};
use crate::model::{Asn, Error, Field, Model, PeekableTokens, Type};
use crate::parser::Token;
use std::convert::TryFrom;
use std::iter::Peekable;
Expand Down Expand Up @@ -55,7 +55,8 @@ impl ComponentTypeList<Unresolved> {
pub fn try_resolve<
R: Resolver<<Resolved as ResolveState>::SizeType>
+ Resolver<<Resolved as ResolveState>::RangeType>
+ Resolver<<Resolved as ResolveState>::ConstType>,
+ Resolver<<Resolved as ResolveState>::ConstType>
+ Resolver<Type<Unresolved>>,
>(
&self,
resolver: &R,
Expand Down
20 changes: 19 additions & 1 deletion asn1rs-model/src/model/lor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::model::{Asn, Definition, LiteralValue, Model, ValueReference};
use crate::model::{Asn, Definition, LiteralValue, Model, Type, ValueReference};
use std::fmt::{Debug, Display, Formatter};

pub trait ResolveState: Clone {
Expand Down Expand Up @@ -52,6 +52,7 @@ where

#[derive(Debug, PartialOrd, PartialEq)]
pub enum Error {
FailedToResolveType(String),
FailedToResolveReference(String),
FailedToParseLiteral(String),
}
Expand All @@ -60,6 +61,9 @@ impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::FailedToResolveType(name) => {
write!(f, "Failed to resolve type with name: {}", name)
}
Error::FailedToResolveReference(name) => {
write!(f, "Failed to resolve reference with name: {}", name)
}
Expand Down Expand Up @@ -157,6 +161,20 @@ impl Resolver<LiteralValue> for Model<Asn<Unresolved>> {
}
}

impl Resolver<Type<Unresolved>> for Model<Asn<Unresolved>> {
fn resolve(&self, lor: &LitOrRef<Type<Unresolved>>) -> Result<Type<Unresolved>, Error> {
match lor {
LitOrRef::Lit(lit) => Ok(lit.clone()),
LitOrRef::Ref(name) => self
.definitions
.iter()
.find(|def| def.0.eq(name))
.map(|def| def.1.r#type.clone())
.ok_or_else(|| Error::FailedToResolveType(name.clone())),
}
}
}

#[cfg(test)]
pub mod tests {
use super::*;
Expand Down
4 changes: 3 additions & 1 deletion asn1rs-model/src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ pub enum LiteralValue {
String(String),
Integer(i64),
OctetString(Vec<u8>),
EnumeratedVariant(String, String),
}

impl LiteralValue {
Expand Down Expand Up @@ -631,7 +632,8 @@ impl Field<Asn<Unresolved>> {
pub fn try_resolve<
R: Resolver<<Resolved as ResolveState>::SizeType>
+ Resolver<<Resolved as ResolveState>::RangeType>
+ Resolver<<Resolved as ResolveState>::ConstType>,
+ Resolver<<Resolved as ResolveState>::ConstType>
+ Resolver<Type<Unresolved>>,
>(
&self,
resolver: &R,
Expand Down
8 changes: 8 additions & 0 deletions asn1rs-model/src/model/rust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,14 @@ impl LiteralValue {
}
write!(f, "]")
}
LiteralValue::EnumeratedVariant(r#type, variant) => {
write!(
f,
"{}::{}",
rust_struct_or_enum_name(r#type),
rust_variant_name(variant)
)
}
}
}
}
Expand Down
70 changes: 70 additions & 0 deletions tests/resolve_enumerated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
mod test_utils;

use test_utils::*;

asn_to_rust!(
r"BasicEnumerated DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
Basic ::= ENUMERATED {
abc,
def,
ghi
}
Container ::= SEQUENCE {
the-selection Basic DEFAULT abc
}
END"
);
#[test]
pub fn does_it_compile() {
let _ = Basic::Abc;
let _ = Basic::Def;
let _ = Basic::Ghi;
let seq = Container {
the_selection: Basic::Def,
};

PrintlnWriter::default().write(&seq).unwrap();
// Writing sequence MyCleverSeq, tag=Universal(16)
// Writing DEFAULT (default: 1337)
// Some
// WRITING Integer(MIN..MAX), tag=ContextSpecific(0)
// 5
}

#[test]
pub fn the_selection_abc() {
serialize_and_deserialize_uper(
1,
&[0x00],
&Container {
the_selection: Basic::Abc,
},
);
}

#[test]
pub fn the_selection_def() {
serialize_and_deserialize_uper(
3,
&[0xA0],
&Container {
the_selection: Basic::Def,
},
);
}

#[test]
pub fn the_selection_ghi() {
serialize_and_deserialize_uper(
3,
&[0xC0],
&Container {
the_selection: Basic::Ghi,
},
);
}

0 comments on commit 0c1cd81

Please sign in to comment.