Skip to content

Commit

Permalink
Add optional From impl generation for CHOICE
Browse files Browse the repository at this point in the history
This adds the configuration option `generate_from_impls` which emits
`From` impls for all generated enum variants mapping to an ASN1 CHOICE.

Closes #34.
  • Loading branch information
shahn committed Sep 11, 2024
1 parent 1796499 commit d10bf72
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 4 deletions.
40 changes: 40 additions & 0 deletions rasn-compiler-tests/tests/edge_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,46 @@ e2e_pdu!(
} "#
);

e2e_pdu!(
distinguished_value_range_in_choice_from_impl,
rasn_compiler::prelude::RasnConfig {
generate_from_impls: true,
..Default::default()
},
r#"
TestChoice ::= CHOICE {
restricted Distinguished (second|fourth..sixth|eighth),
...
}
Distinguished ::= INTEGER {
first(1),
second(2),
third(3),
fourth(4),
fifth(5),
sixth(6),
seventh(7),
eighth(8),
ninth(9),
tenth(10),
} (1..10)"#,
r#" #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(delegate, value("1..=10"))]
pub struct Distinguished(pub u8);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum TestChoice {
#[rasn(value("2..=8"))]
restricted(Distinguished),
}
impl From<Distinguished> for TestChoice {
fn from(value: Distinguished) -> Self {
Self::restricted(value)
}
} "#
);

e2e_pdu!(
enum_and_distinguished_defaults,
r#"
Expand Down
52 changes: 52 additions & 0 deletions rasn-compiler-tests/tests/structured_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,55 @@ e2e_pdu!(
pub static ref NESTED_TYPE_VAL: NestedType = NestedType::new(NestedTypeChoiceField::one(Integer::from(4)));
} "#
);

e2e_pdu!(
nested_choice_value_from_impl,
rasn_compiler::prelude::RasnConfig {
generate_from_impls: true,
..Default::default()
},
r#"
NestedType ::= SEQUENCE {
choiceField CHOICE {
one INTEGER,
two BOOLEAN
}
}
nestedTypeVal NestedType ::= { choiceField one:4 }
"#,
r#"
#[doc = "Inner type"]
#[derive(AsnType,Debug,Clone,Decode,Encode,PartialEq)]
#[rasn(choice, automatic_tags)]
pub enum NestedTypeChoiceField {
one(Integer),
two(bool),
}
impl From<Integer> for NestedTypeChoiceField {
fn from(value: Integer) -> Self {
Self::one(value)
}
}
impl From<bool> for NestedTypeChoiceField {
fn from(value: bool) -> Self {
Self::two(value)
}
}
#[derive(AsnType,Debug,Clone,Decode,Encode,PartialEq)]
#[rasn(automatic_tags)]
pub struct NestedType{
#[rasn(identifier="choiceField")]
pub choice_field: NestedTypeChoiceField,
}
impl NestedType {
pub fn new(choice_field: NestedTypeChoiceField) -> Self {
Self { choice_field }
}
}
lazy_static! {
pub static ref NESTED_TYPE_VAL: NestedType = NestedType::new(NestedTypeChoiceField::one(Integer::from(4)));
} "#
);
23 changes: 20 additions & 3 deletions rasn-compiler/src/generator/rasn/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,14 +725,31 @@ impl Rasn {
&tld.ty,
));
}
Ok(choice_template(
let mut choice_str = choice_template(
self.format_comments(&tld.comments)?,
name.clone(),
&name,
extensible,
self.format_choice_options(choice, &name.to_string())?,
inner_options,
self.join_annotations(annotations),
))
);
if self.config.generate_from_impls {
choice_str = std::iter::once(choice_str)
.map(|x| Ok(x))
.chain(choice.options.iter().map(|o| {
let (_, formatted_type_name) =
self.constraints_and_type_name(&o.ty, &o.name, &name.to_string())?;

let o_name = self.to_rust_enum_identifier(&o.name);
Ok::<_, GeneratorError>(choice_from_impl_template(
&name,
o_name,
formatted_type_name,
))
}))
.collect::<Result<TokenStream, _>>()?;
}
Ok(choice_str)
} else {
Err(GeneratorError::new(
Some(ToplevelDefinition::Type(tld)),
Expand Down
6 changes: 6 additions & 0 deletions rasn-compiler/src/generator/rasn/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ pub struct Config {
/// is set to `true` , the compiler will import the entire module using
/// the wildcard `*` for each module that the input ASN.1 module imports from.
pub default_wildcard_imports: bool,
/// To make working with the generated types a bit more ergonomic, the compiler
/// can generate `From` impls for a `CHOICE`. This is disabled by default to
/// generate less code, but can be enabled with `generate_from_impls` set to
/// `true`.
pub generate_from_impls: bool,
}

#[cfg(target_family = "wasm")]
Expand All @@ -65,6 +70,7 @@ impl Default for Config {
Self {
opaque_open_types: true,
default_wildcard_imports: false,
generate_from_impls: false,
}
}
}
Expand Down
16 changes: 15 additions & 1 deletion rasn-compiler/src/generator/rasn/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ pub fn const_choice_value_template(

pub fn choice_template(
comments: TokenStream,
name: TokenStream,
name: &TokenStream,
extensible: TokenStream,
options: TokenStream,
nested_options: Vec<TokenStream>,
Expand All @@ -329,3 +329,17 @@ pub fn choice_template(
}
}
}

pub fn choice_from_impl_template(
name: &TokenStream,
variant: Ident,
wrapped: TokenStream,
) -> TokenStream {
quote! {
impl From<#wrapped> for #name {
fn from(value: #wrapped) -> Self {
Self::#variant(value)
}
}
}
}

0 comments on commit d10bf72

Please sign in to comment.