From 95d2953a34d9f1342d4b519a059b5337ab987df3 Mon Sep 17 00:00:00 2001 From: Michael Watzko Date: Thu, 7 May 2020 17:01:14 +0200 Subject: [PATCH] Write and parse extensible_after for CHOICE and ENUMERATED #11 --- asn1rs-model/src/ast/attribute.rs | 177 ++++++++++++++++++++++----- asn1rs-model/src/ast/mod.rs | 182 +++++++++++++++++----------- asn1rs-model/src/gen/rust/mod.rs | 129 +++++++++++--------- asn1rs-model/src/gen/rust/uper.rs | 10 +- asn1rs-model/src/gen/rust/walker.rs | 6 +- asn1rs-model/src/model/mod.rs | 53 +++++++- asn1rs-model/src/model/rust.rs | 45 ++++--- tests/proc_macro_reparse.rs | 16 +++ 8 files changed, 440 insertions(+), 178 deletions(-) diff --git a/asn1rs-model/src/ast/attribute.rs b/asn1rs-model/src/ast/attribute.rs index 61c03a09..adff4390 100644 --- a/asn1rs-model/src/ast/attribute.rs +++ b/asn1rs-model/src/ast/attribute.rs @@ -1,20 +1,36 @@ use super::range::ident_or_literal_or_punct; use super::range::MaybeRanged; use super::tag::AttrTag; -use crate::model::{Range, Tag, Type}; +use crate::model::{Choice, ChoiceVariant, Enumerated, EnumeratedVariant, Range, Tag, Type}; +use std::fmt::Display; +use std::marker::PhantomData; +use std::ops::Deref; use syn::parenthesized; use syn::parse::{Parse, ParseBuffer}; -#[derive(Debug, Default)] -pub(crate) struct AsnAttribute { - pub(crate) r#type: Option, +#[derive(Debug)] +pub(crate) struct AsnAttribute { + pub(crate) primary: C::Primary, pub(crate) tag: Option, - pub(crate) number: Option, + pub(crate) extensible_after: Option, + _c: PhantomData, } -impl Parse for AsnAttribute { +impl AsnAttribute { + pub fn new(primary: C::Primary) -> Self { + Self { + primary, + tag: None, + extensible_after: None, + _c: Default::default(), + } + } +} + +impl Parse for AsnAttribute { fn parse<'a>(input: &'a ParseBuffer<'a>) -> syn::Result { - let mut asn = Self::default(); + let mut asn = Self::new(C::Primary::parse(input)?); + eof_or_comma(&input, "Primary attribute must be separated by comma")?; while !input.cursor().eof() { let lowercase_ident = input @@ -25,23 +41,17 @@ impl Parse for AsnAttribute { .to_lowercase(); match lowercase_ident.as_str() { - // variant number - _ if asn.number.is_none() && lowercase_ident.chars().all(char::is_numeric) => { - asn.number = Some( - lowercase_ident - .parse() - .map_err(|e| input.error(format!("Invalid number: {}", e)))?, - ); - } - // field type - _ if asn.r#type.is_none() => { - asn.r#type = Some(parse_type_pre_stepped(&lowercase_ident, input)?); - } - // --- additional properties - "tag" => { + "tag" if C::TAGGABLE => { let tag = AttrTag::parse(input)?; asn.tag = Some(tag.0); } + "extensible_after" if C::EXTENSIBLE => { + let content; + parenthesized!(content in input); + let ident = content + .step(|s| s.ident().ok_or_else(|| content.error("Not a valid ident")))?; + asn.extensible_after = Some(ident.to_string()); + } attribute => { return Err( input.error(format!("Unexpected or repeated attribute: `{}`", attribute)) @@ -49,14 +59,7 @@ impl Parse for AsnAttribute { } } - if !input.cursor().eof() && !input.peek(syn::token::Comma) { - return Err(input.error("Attributes must be separated by comma")); - } else if !input.cursor().eof() { - let _ = input.step(|c| { - c.punct() - .ok_or_else(|| input.error("Attributes must be separated by comma")) - })?; - } + eof_or_comma(input, "Attributes must be separated by comma")?; } Ok(asn) } @@ -104,3 +107,119 @@ fn parse_type_pre_stepped<'a>( r#type => Err(input.error(format!("Unexpected attribute: `{}`", r#type))), } } + +fn eof_or_comma(input: &ParseBuffer, msg: T) -> syn::Result<()> { + if !input.cursor().eof() && !input.peek(syn::token::Comma) { + Err(input.error(msg)) + } else if !input.cursor().eof() { + // skip the comma + input + .step(|c| c.punct().ok_or_else(|| input.error(msg))) + .map(drop) + } else { + // eof + Ok(()) + } +} + +pub trait PrimaryContext: Sized { + fn parse(input: &ParseBuffer<'_>) -> syn::Result; +} + +impl PrimaryContext for Type { + fn parse(input: &ParseBuffer<'_>) -> syn::Result { + let lowercase_ident = input + .step(|c| { + ident_or_literal_or_punct(*c) + .ok_or_else(|| c.error("Expected type, number or extension marker")) + })? + .to_string() + .to_lowercase(); + + Ok(parse_type_pre_stepped(&lowercase_ident, input)?) + } +} + +impl PrimaryContext for Option { + fn parse(input: &ParseBuffer<'_>) -> syn::Result { + input + .step(|c| { + ident_or_literal_or_punct(*c) + .ok_or_else(|| c.error("Expected type, number or extension marker")) + }) + .ok() + .as_ref() + .map(ToString::to_string) + .as_ref() + .map(String::as_str) + .map(str::to_lowercase) + .map(|lowercase_ident| { + lowercase_ident + .parse() + .map_err(|e| input.error(format!("Invalid number: {}", e))) + }) + .transpose() + } +} + +pub trait Context { + type Primary: PrimaryContext; + const EXTENSIBLE: bool; + const TAGGABLE: bool; +} + +impl Context for Choice { + type Primary = Type; + const EXTENSIBLE: bool = true; + const TAGGABLE: bool = true; +} + +impl Context for ChoiceVariant { + type Primary = Type; + const EXTENSIBLE: bool = false; + const TAGGABLE: bool = true; +} + +impl Context for Enumerated { + type Primary = Type; + const EXTENSIBLE: bool = true; + const TAGGABLE: bool = true; +} + +impl Context for EnumeratedVariant { + type Primary = Option; + const EXTENSIBLE: bool = false; + const TAGGABLE: bool = false; +} + +pub struct Transparent; +impl Context for Transparent { + type Primary = Type; + const EXTENSIBLE: bool = false; + const TAGGABLE: bool = true; +} + +pub struct DefinitionHeader(String); + +impl Deref for DefinitionHeader { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Context for DefinitionHeader { + type Primary = Self; + const EXTENSIBLE: bool = true; + const TAGGABLE: bool = true; +} + +impl PrimaryContext for DefinitionHeader { + fn parse(input: &ParseBuffer<'_>) -> syn::Result { + input + .step(|c| c.ident().ok_or_else(|| c.error("Expected type identifier"))) + .map(|ident| ident.to_string()) + .map(DefinitionHeader) + } +} diff --git a/asn1rs-model/src/ast/mod.rs b/asn1rs-model/src/ast/mod.rs index d03866fd..f0287507 100644 --- a/asn1rs-model/src/ast/mod.rs +++ b/asn1rs-model/src/ast/mod.rs @@ -2,7 +2,8 @@ mod attribute; mod range; mod tag; -use crate::model::{Asn as AsnModelType, EnumeratedVariant}; +use crate::ast::attribute::{Context, DefinitionHeader, Transparent}; +use crate::model::{Asn as AsnModelType, EnumeratedVariant, TagProperty}; use crate::model::{Choice, ChoiceVariant, Definition, Enumerated, Field, Model, Type}; use attribute::AsnAttribute; use quote::quote; @@ -10,8 +11,7 @@ use std::convert::Infallible; use std::str::FromStr; use syn::export::TokenStream2 as TokenStream; use syn::spanned::Spanned; -use syn::Meta; -use syn::{Attribute, Item, NestedMeta}; +use syn::{Attribute, Item}; pub fn parse(attr: TokenStream, item: TokenStream) -> TokenStream { if cfg!(feature = "debug-proc-macro") { @@ -77,46 +77,42 @@ pub fn parse_asn_definition( attr: TokenStream, item: TokenStream, ) -> Result<(Option>, Item), TokenStream> { + let item_span = item.span(); let attr_span = attr.span(); - let attribute = syn::parse2::(attr); - let item = syn::parse2::(item).unwrap(); - - let asn_type_decl = match attribute { - Err(e) => { - return Err(compile_error_ts( - attr_span, - format!("Missing ASN attribute: {}", e), - )) - } - Ok(NestedMeta::Meta(Meta::Path(path))) => path - .segments - .iter() - .next() - .expect("Missing ASN Attribute in path") - .ident - .to_string() - .to_lowercase(), - _ => return Err(compile_error_ts(attr_span, "Invalid ASN Attribute type")), - }; - parse_item_definition(item, &asn_type_decl) -} + println!("ATTRIBUTE: {}", attr.to_string()); + println!("ITEM: {}", item.to_string()); + + let item = syn::parse2::(item) + .map_err(|e| compile_error_ts(item_span, format!("Invalid Item: {}", e)))?; + let asn = syn::parse2::>(attr.clone()).map_err(|e| { + compile_error_ts( + attr_span, + format!("Invalid ASN attribute ('{}'): {}", attr.to_string(), e), + ) + })?; -fn parse_item_definition( - item: syn::Item, - asn_type_decl: &str, -) -> Result<(Option>, Item), TokenStream> { match item { - Item::Struct(strct) if asn_type_decl == "sequence" => parse_sequence(strct), - Item::Struct(strct) if asn_type_decl == "transparent" => parse_transparent(strct), - Item::Enum(enm) if asn_type_decl == "enumerated" => parse_enumerated(enm), - Item::Enum(enm) if asn_type_decl == "choice" => parse_choice(enm), + Item::Struct(strct) if asn.primary.eq_ignore_ascii_case("sequence") => { + parse_sequence(strct, &asn, attr_span) + } + Item::Struct(strct) if asn.primary.eq_ignore_ascii_case("transparent") => { + parse_transparent(strct, &asn, attr_span) + } + Item::Enum(enm) if asn.primary.eq_ignore_ascii_case("enumerated") => { + parse_enumerated(enm, &asn, attr_span) + } + Item::Enum(enm) if asn.primary.eq_ignore_ascii_case("choice") => { + parse_choice(enm, &asn, attr_span) + } item => Ok((None, item)), } } fn parse_sequence( mut strct: syn::ItemStruct, + asn: &AsnAttribute, + _asn_span: proc_macro2::Span, ) -> Result<(Option>, Item), TokenStream> { let fields = strct .fields @@ -129,18 +125,22 @@ fn parse_sequence( )?; } - parse_and_remove_first_asn_attribute_type(field.span(), &field.ty, &mut field.attrs) - .map(|asn| Field { - name: field.ident.as_ref().unwrap().to_string(), - role: asn, - }) + parse_and_remove_first_asn_attribute_type::( + field.span(), + &field.ty, + &mut field.attrs, + ) + .map(|asn| Field { + name: field.ident.as_ref().unwrap().to_string(), + role: asn, + }) }) .vec_result()?; Ok(( Some(Definition( strct.ident.to_string(), - Type::Sequence(fields).untagged(), + Type::Sequence(fields).opt_tagged(asn.tag), )), Item::Struct(strct), )) @@ -148,6 +148,8 @@ fn parse_sequence( fn parse_transparent( mut strct: syn::ItemStruct, + asn: &AsnAttribute, + _asn_span: proc_macro2::Span, ) -> Result<(Option>, Item), TokenStream> { if strct.fields.len() != 1 || strct.fields.iter().next().unwrap().ident.is_some() { compile_err_ts( @@ -157,18 +159,26 @@ fn parse_transparent( } let field = strct.fields.iter_mut().next().unwrap(); - parse_and_remove_first_asn_attribute_type(field.span(), &field.ty, &mut field.attrs).map( - |asn| { - ( - Some(Definition(strct.ident.to_string(), asn)), - Item::Struct(strct), - ) - }, + parse_and_remove_first_asn_attribute_type::( + field.span(), + &field.ty, + &mut field.attrs, ) + .map(|parsed| { + ( + Some(Definition( + strct.ident.to_string(), + parsed.with_tag_opt(asn.tag), + )), + Item::Struct(strct), + ) + }) } fn parse_enumerated( mut enm: syn::ItemEnum, + asn: &AsnAttribute, + asn_span: proc_macro2::Span, ) -> Result<(Option>, Item), TokenStream> { enm.variants .iter() @@ -186,15 +196,16 @@ fn parse_enumerated( .iter_mut() .map(|v| { let variant = EnumeratedVariant::from_name(v.ident.to_string()); - let attributes = index_of_first_asn_attribute(&v.attrs) - .map(|_index| parse_and_remove_first_asn_attribute(v.span(), &mut v.attrs)); + let attributes = index_of_first_asn_attribute(&v.attrs).map(|_index| { + parse_and_remove_first_asn_attribute::(v.span(), &mut v.attrs) + }); if let Some(attributes) = attributes { attributes.and_then(|attr| { if attr.tag.is_some() { compile_err_ts(v.span(), "ENUMERATED Variants must not have a Tag")?; } - Ok(variant.with_number_opt(attr.number)) + Ok(variant.with_number_opt(attr.primary)) }) } else { Ok(variant) @@ -202,14 +213,13 @@ fn parse_enumerated( }) .vec_result()?; - // TODO extensible - // TODO tags - let enumerated = Enumerated::from_variants(variants); + let extension_after = find_extensible_index(asn, asn_span, variants.iter().map(|v| v.name()))?; + let enumerated = Enumerated::from_variants(variants).with_extension_after(extension_after); Ok(( Some(Definition( enm.ident.to_string(), - Type::Enumerated(enumerated).untagged(), + Type::Enumerated(enumerated).opt_tagged(asn.tag), )), Item::Enum(enm), )) @@ -217,6 +227,8 @@ fn parse_enumerated( fn parse_choice( mut enm: syn::ItemEnum, + asn: &AsnAttribute, + asn_span: proc_macro2::Span, ) -> Result<(Option>, Item), TokenStream> { enm.variants .iter() @@ -240,7 +252,7 @@ fn parse_choice( )?; } - parse_and_remove_first_asn_attribute_type( + parse_and_remove_first_asn_attribute_type::( v.span(), &v.fields.iter().next().unwrap().ty, &mut v.attrs, @@ -257,31 +269,61 @@ fn parse_choice( }) .vec_result()?; + let extensible_after = + find_extensible_index(&asn, asn_span, variants.iter().map(|v| v.name()))?; + Ok(( Some(Definition( enm.ident.to_string(), - Type::Choice(Choice::from_variants(variants)).untagged(), + Type::Choice( + Choice::from_variants(variants.into_iter()).with_extension_after(extensible_after), + ) + .opt_tagged(asn.tag), )), Item::Enum(enm), )) } -fn parse_and_remove_first_asn_attribute_type( +fn find_extensible_index( + asn: &AsnAttribute, + asn_span: proc_macro2::Span, + variants: impl Iterator>, +) -> Result, TokenStream> { + asn.extensible_after + .as_ref() + .map(|name| { + variants + .enumerate() + .find_map(|(index, v)| { + if v.as_ref().eq(name) { + Some(index) + } else { + None + } + }) + .ok_or_else(|| { + compile_error_ts(asn_span, "Cannot find variant for extensible attribute") + }) + }) + .transpose() +} + +fn parse_and_remove_first_asn_attribute_type>( span: proc_macro2::Span, ty: &syn::Type, attrs: &mut Vec, ) -> Result { - parse_and_remove_first_asn_attribute(span, attrs) + parse_and_remove_first_asn_attribute::(span, attrs) .and_then(|asn| into_asn_or_err(span, &ty, asn)) } -fn parse_and_remove_first_asn_attribute( +fn parse_and_remove_first_asn_attribute( span: proc_macro2::Span, attrs: &mut Vec, -) -> Result { +) -> Result, TokenStream> { find_and_remove_first_asn_attribute_or_err(span, attrs).and_then(|attribute| { attribute - .parse_args::() + .parse_args::>() .map_err(|e| e.to_compile_error()) }) } @@ -289,23 +331,21 @@ fn parse_and_remove_first_asn_attribute( fn into_asn_or_err( span: proc_macro2::Span, ty: &syn::Type, - asn: AsnAttribute, + asn: AsnAttribute>, ) -> Result { into_asn(ty, asn).ok_or_else(|| compile_error_ts(span, "Missing ASN-Type")) } -fn into_asn(ty: &syn::Type, asn: AsnAttribute) -> Option { +fn into_asn>( + ty: &syn::Type, + asn: AsnAttribute, +) -> Option { Some(AsnModelType { tag: asn.tag, - r#type: match asn.r#type { - Some(some) => { - if let Type::TypeReference(_) = some { - Type::TypeReference(quote! { #ty }.to_string()) - } else { - some - } - } - None => return None, + r#type: if let Type::TypeReference(_) = asn.primary { + Type::TypeReference(quote! { #ty }.to_string()) + } else { + asn.primary }, }) } diff --git a/asn1rs-model/src/gen/rust/mod.rs b/asn1rs-model/src/gen/rust/mod.rs index bc679a00..e4ddcd7c 100644 --- a/asn1rs-model/src/gen/rust/mod.rs +++ b/asn1rs-model/src/gen/rust/mod.rs @@ -21,7 +21,7 @@ use crate::model::Range; use crate::model::Rust; use crate::model::RustType; use crate::model::TagProperty; -use crate::model::{Asn, Definition, Tag, Type as AsnType, Type}; +use crate::model::{Definition, Tag, Type as AsnType, Type}; use codegen::Block; use codegen::Enum; use codegen::Function; @@ -171,24 +171,41 @@ impl RustCodeGenerator { pub fn add_definition(&self, scope: &mut Scope, Definition(name, rust): &Definition) { match rust { - Rust::Struct(fields) => Self::add_struct( - self.new_struct(scope, name, false), - name, - fields, - self.direct_field_access, - ), - Rust::Enum(variants) => { - Self::add_enum(self.new_enum(scope, name, true), name, variants) + Rust::Struct(fields) => { + scope.raw(&Self::asn_attribute("sequence", None, None)); + Self::add_struct( + self.new_struct(scope, name), + name, + fields, + self.direct_field_access, + ) } - Rust::DataEnum(enumeration) => { - Self::add_data_enum(self.new_enum(scope, name, false), name, enumeration) + Rust::Enum(plain) => { + scope.raw(&Self::asn_attribute( + "enumerated", + None, + plain.extension_after_variant().map(|v| v.clone()), + )); + Self::add_enum(self.new_enum(scope, name, true), name, plain) + } + Rust::DataEnum(data) => { + scope.raw(&Self::asn_attribute( + "choice", + None, + data.extension_after_variant().map(|v| v.name().to_string()), + )); + Self::add_data_enum(self.new_enum(scope, name, false), name, data) + } + Rust::TupleStruct(inner) => { + scope.raw(&Self::asn_attribute("transparent", None, None)); + Self::add_tuple_struct( + self.new_struct(scope, name), + name, + inner, + self.direct_field_access, + None, + ) } - Rust::TupleStruct(inner) => Self::add_tuple_struct( - self.new_struct(scope, name, true), - name, - inner, - self.direct_field_access, - ), } } @@ -202,7 +219,11 @@ impl RustCodeGenerator { str_ct.field( &format!( "{} {}{}", - Self::asn_attribute(&field_type.clone().into_asn().untagged()), + Self::asn_attribute( + &Self::asn_attribute_type(&field_type.clone().into_asn()), + None, // TODO missing tag + None + ), if pub_access { "pub " } else { "" }, Self::rust_field_name(field_name, true), ), @@ -222,11 +243,9 @@ impl RustCodeGenerator { en_m.new_variant(&format!( "{} {}({})", Self::asn_attribute( - &variant - .r#type() - .clone() - .into_asn() - .opt_tagged(variant.tag()) + Self::asn_attribute_type(&variant.r#type().clone().into_asn()), + variant.tag(), + None ), Self::rust_variant_name(variant.name()), variant.r#type().to_string(), @@ -234,26 +253,41 @@ impl RustCodeGenerator { } } - fn add_tuple_struct(str_ct: &mut Struct, _name: &str, inner: &RustType, pub_access: bool) { - // TODO assuming untagged + fn add_tuple_struct( + str_ct: &mut Struct, + _name: &str, + inner: &RustType, + pub_access: bool, + tag: Option, + ) { str_ct.tuple_field(format!( "{} {}{}", - Self::asn_attribute(&inner.clone().into_asn().untagged()), + Self::asn_attribute( + Self::asn_attribute_type(&inner.clone().into_asn()), + tag, + None + ), if pub_access { "pub " } else { "" }, inner.to_string(), )); } - fn asn_attribute(asn: &Asn) -> String { + fn asn_attribute( + r#type: T, + tag: Option, + extensible_after: Option, + ) -> String { format!( - "#[asn({}{}{})]", - Self::asn_attribute_type(&asn.r#type), - if asn.tag.is_some() { ", " } else { "" }, - if let Some(tag) = &asn.tag { - Self::asn_attribute_tag(tag) - } else { - String::default() - } + "#[asn({})]", + vec![ + Some(r#type.to_string()), + tag.map(Self::asn_attribute_tag), + extensible_after.map(Self::asn_attribute_extensible_after) + ] + .into_iter() + .flatten() + .collect::>() + .join(", ") ) } @@ -275,7 +309,7 @@ impl RustCodeGenerator { } } - fn asn_attribute_tag(tag: &Tag) -> String { + fn asn_attribute_tag(tag: Tag) -> String { match tag { Tag::Universal(t) => format!("tag(UNIVERSAL({}))", t), Tag::Application(t) => format!("tag(APPLICATION({}))", t), @@ -284,6 +318,10 @@ impl RustCodeGenerator { } } + fn asn_attribute_extensible_after(variant: String) -> String { + format!("extensible_after({})", variant) + } + fn impl_definition( scope: &mut Scope, Definition(name, rust): &Definition, @@ -656,20 +694,7 @@ impl RustCodeGenerator { out } - fn new_struct<'a>( - &self, - scope: &'a mut Scope, - name: &str, - asn_transparent: bool, - ) -> &'a mut Struct { - scope.raw(&format!( - "#[asn({})]", - if asn_transparent { - "transparent" - } else { - "sequence" - } - )); + fn new_struct<'a>(&self, scope: &'a mut Scope, name: &str) -> &'a mut Struct { let str_ct = scope .new_struct(name) .vis("pub") @@ -685,10 +710,6 @@ impl RustCodeGenerator { } fn new_enum<'a>(&self, scope: &'a mut Scope, name: &str, c_enum: bool) -> &'a mut Enum { - scope.raw(&format!( - "#[asn({})]", - if c_enum { "enumerated" } else { "choice" } - )); let en_m = scope .new_enum(name) .vis("pub") diff --git a/asn1rs-model/src/gen/rust/uper.rs b/asn1rs-model/src/gen/rust/uper.rs index a637b753..417a630c 100644 --- a/asn1rs-model/src/gen/rust/uper.rs +++ b/asn1rs-model/src/gen/rust/uper.rs @@ -216,7 +216,7 @@ impl UperSerializer { } fn impl_read_fn_for_enum(function: &mut Function, name: &str, r_enum: &PlainEnum) { - if let Some(last_standard_index) = r_enum.last_standard_index() { + if let Some(last_standard_index) = r_enum.extension_after_index() { function.line(format!( "let id = reader.read_choice_index_extensible({})? as i64;", last_standard_index + 1 @@ -240,7 +240,7 @@ impl UperSerializer { fn impl_read_fn_for_data_enum(function: &mut Function, name: &str, enumeration: &DataEnum) { if enumeration.len() > 1 { - if let Some(last_standard_index) = enumeration.last_standard_index() { + if let Some(last_standard_index) = enumeration.extension_after_index() { function.line(&format!( "let variant = reader.read_choice_index_extensible({})? as i64;", last_standard_index + 1 @@ -493,7 +493,7 @@ impl UperSerializer { fn impl_write_fn_for_enum(function: &mut Function, name: &str, r_enum: &PlainEnum) { let mut block = Block::new("match self"); for (i, variant) in r_enum.variants().enumerate() { - if let Some(last_standard_index) = r_enum.last_standard_index() { + if let Some(last_standard_index) = r_enum.extension_after_index() { block.line(format!( "{}::{} => writer.write_choice_index_extensible({}, {})?,", name, @@ -525,7 +525,7 @@ impl UperSerializer { if enumeration.len() > 1 { let is_extended_variant = Self::is_extended_variant(enumeration, i); - if let Some(last_standard_index) = enumeration.last_standard_index() { + if let Some(last_standard_index) = enumeration.extension_after_index() { block_case.line(format!( "writer.write_choice_index_extensible({}, {})?;", i, @@ -575,7 +575,7 @@ impl UperSerializer { fn is_extended_variant(enumeration: &Enumeration, variant: usize) -> bool { enumeration - .last_standard_index() + .extension_after_index() .map(|last| variant > last) .unwrap_or(false) } diff --git a/asn1rs-model/src/gen/rust/walker.rs b/asn1rs-model/src/gen/rust/walker.rs index 90170549..2ba1fc64 100644 --- a/asn1rs-model/src/gen/rust/walker.rs +++ b/asn1rs-model/src/gen/rust/walker.rs @@ -272,7 +272,7 @@ impl AsnDefWriter { format!( "const STD_VARIANT_COUNT: usize = {};", enumerated - .last_standard_index() + .extension_after_index() .unwrap_or_else(|| enumerated.len()) ), format!("const EXTENSIBLE: bool = {};", enumerated.is_extensible()), @@ -341,7 +341,9 @@ impl AsnDefWriter { format!("const VARIANT_COUNT: usize = {};", choice.len()), format!( "const STD_VARIANT_COUNT: usize = {};", - choice.last_standard_index().unwrap_or_else(|| choice.len()) + choice + .extension_after_index() + .unwrap_or_else(|| choice.len()) ), format!("const EXTENSIBLE: bool = {};", choice.is_extensible()), ], diff --git a/asn1rs-model/src/model/mod.rs b/asn1rs-model/src/model/mod.rs index fddf71c2..38e5b43c 100644 --- a/asn1rs-model/src/model/mod.rs +++ b/asn1rs-model/src/model/mod.rs @@ -641,6 +641,26 @@ impl Asn { pub const fn tagged(tag: Tag, r#type: Type) -> Self { Self::opt_tagged(Some(tag), r#type) } + + pub fn extensible_after_index(&self) -> Option { + match &self.r#type { + Type::Choice(c) => c.extension_after_index(), + Type::Enumerated(e) => e.extension_after_index(), + _ => None, + } + } + + pub fn extensible_after_variant(&self) -> Option<&str> { + match &self.r#type { + Type::Choice(c) => c + .extension_after_index() + .and_then(|index| c.variants().nth(index).map(ChoiceVariant::name)), + Type::Enumerated(e) => e + .extension_after_index() + .and_then(|index| e.variants().nth(index).map(EnumeratedVariant::name)), + _ => None, + } + } } impl From for Asn { @@ -703,13 +723,27 @@ pub struct Choice { extension_after: Option, } -impl Choice { - pub fn from_variants(variants: Vec) -> Self { +impl From> for Choice { + fn from(variants: Vec) -> Self { Self { variants, extension_after: None, } } +} + +impl Choice { + pub fn from_variants(variants: impl Iterator) -> Self { + Self { + variants: variants.collect(), + extension_after: None, + } + } + + pub const fn with_extension_after(mut self, extension_after: Option) -> Self { + self.extension_after = extension_after; + self + } pub fn len(&self) -> usize { self.variants.len() @@ -826,6 +860,15 @@ pub struct Enumerated { extension_after: Option, } +impl From> for Enumerated { + fn from(variants: Vec) -> Self { + Self { + variants, + extension_after: None, + } + } +} + impl Enumerated { pub fn from_variants(variants: impl Into>) -> Self { Self { @@ -947,6 +990,10 @@ impl EnumeratedVariant { } } + pub const fn with_number(self, number: usize) -> Self { + self.with_number_opt(Some(number)) + } + pub const fn with_number_opt(mut self, number: Option) -> Self { self.number = number; self @@ -1184,7 +1231,7 @@ pub(crate) mod tests { "Woah".into(), Type::Sequence(vec![Field { name: "decision".into(), - role: Type::Choice(Choice::from_variants(vec![ + role: Type::Choice(Choice::from(vec![ ChoiceVariant::name_type("this", Type::TypeReference("This".into())), ChoiceVariant::name_type("that", Type::TypeReference("That".into())), ChoiceVariant::name_type("neither", Type::TypeReference("Neither".into())), diff --git a/asn1rs-model/src/model/rust.rs b/asn1rs-model/src/model/rust.rs index b53983f8..1c608513 100644 --- a/asn1rs-model/src/model/rust.rs +++ b/asn1rs-model/src/model/rust.rs @@ -15,7 +15,8 @@ const U16_MAX: u64 = u16::max_value() as u64; const U32_MAX: u64 = u32::max_value() as u64; //const U64_MAX: u64 = u64::max_value() as u64; -pub type PlainEnum = Enumeration; +pub type PlainVariant = String; +pub type PlainEnum = Enumeration; pub type DataEnum = Enumeration; /// Integers are ordered where Ixx < Uxx so @@ -296,7 +297,7 @@ pub struct Enumeration { variants: Vec, extended_after_index: Option, } -#[cfg(test)] + impl From> for Enumeration { fn from(variants: Vec) -> Self { Enumeration { @@ -307,6 +308,11 @@ impl From> for Enumeration { } impl Enumeration { + pub fn with_extension_after(mut self, extension_after: Option) -> Self { + self.extended_after_index = extension_after; + self + } + pub fn len(&self) -> usize { self.variants.len() } @@ -319,15 +325,26 @@ impl Enumeration { self.variants.iter() } - pub fn last_standard_index(&self) -> Option { + pub fn extension_after_index(&self) -> Option { self.extended_after_index } + pub fn extension_after_variant(&self) -> Option<&T> { + self.extended_after_index + .and_then(|index| self.variants.iter().nth(index)) + } + pub fn is_extensible(&self) -> bool { self.extended_after_index.is_some() } } +impl PlainEnum { + pub fn from_names(names: impl Iterator) -> Self { + Self::from(names.map(|n| n.to_string()).collect::>()) + } +} + #[derive(Debug, Clone, PartialOrd, PartialEq)] pub struct DataVariant { name_type: (String, RustType), @@ -821,7 +838,7 @@ mod tests { let mut model_asn = Model::default(); model_asn.definitions.push(Definition( "SimpleChoiceTest".into(), - AsnType::Choice(Choice::from_variants(vec![ + AsnType::Choice(Choice::from(vec![ ChoiceVariant::name_type("bernd-das-brot", AsnType::UTF8String), ChoiceVariant::name_type("nochSoEinBrot", AsnType::OctetString), ])) @@ -851,7 +868,7 @@ mod tests { let mut model_asn = Model::default(); model_asn.definitions.push(Definition( "ListChoiceTestWithNestedList".into(), - AsnType::Choice(Choice::from_variants(vec![ + AsnType::Choice(Choice::from(vec![ ChoiceVariant::name_type( "normal-List", AsnType::SequenceOf(Box::new(AsnType::UTF8String)), @@ -1046,10 +1063,10 @@ mod tests { assert_eq!( &[Definition( "Extensible".into(), - Rust::Enum(PlainEnum { - variants: vec!["Abc".to_string(), "Def".to_string(), "Ghi".to_string()], - extended_after_index: Some(2) - }) + Rust::Enum( + PlainEnum::from_names(["Abc", "Def", "Ghi"].iter()) + .with_extension_after(Some(2)) + ) )], &model_rust.definitions[..] ); @@ -1080,15 +1097,15 @@ mod tests { assert_eq!( &[Definition( "Extensible".into(), - Rust::DataEnum(DataEnum { - variants: vec![ + Rust::DataEnum( + DataEnum::from(vec![ DataVariant::from_name_type("Abc".to_string(), RustType::VecU8), DataVariant::from_name_type("Def".to_string(), RustType::U64(None)), DataVariant::from_name_type("Ghi".to_string(), RustType::Bool) .with_tag(Tag::Universal(4)), - ], - extended_after_index: Some(2) - }) + ]) + .with_extension_after(Some(2)) + ) )], &model_rust.definitions[..] ); diff --git a/tests/proc_macro_reparse.rs b/tests/proc_macro_reparse.rs index 866cdc1e..b648828e 100644 --- a/tests/proc_macro_reparse.rs +++ b/tests/proc_macro_reparse.rs @@ -35,6 +35,22 @@ END"#, ) } +#[test] +fn test_extensible_enum() { + parse_asn_map_to_rust_map_to_stringify_with_proc_macro_annotation_re_parse_check_equal( + r#"BasicSchema DEFINITIONS AUTOMATIC TAGS ::= BEGIN + + MyType ::= [UNIVERSAL 5] ENUMERATED { + implicit, + number(7), + ..., + wow + } + +END"#, + ) +} + #[test] fn test_standard_choice() { parse_asn_map_to_rust_map_to_stringify_with_proc_macro_annotation_re_parse_check_equal(