diff --git a/rasn-compiler-tests/src/helpers/mod.rs b/rasn-compiler-tests/src/helpers/mod.rs index 867f4d2..edd9777 100644 --- a/rasn-compiler-tests/src/helpers/mod.rs +++ b/rasn-compiler-tests/src/helpers/mod.rs @@ -13,7 +13,7 @@ macro_rules! e2e_pdu { fn $suite() { rasn_compiler_derive::asn1!($asn1); assert_eq!( - rasn_compiler::Compiler::new() + rasn_compiler::Compiler::::new() .add_asn_literal(&format!("TestModule DEFINITIONS AUTOMATIC TAGS::= BEGIN {} END", $asn1)) .compile_to_string() .unwrap() diff --git a/rasn-compiler-tests/tests/edge_cases.rs b/rasn-compiler-tests/tests/edge_cases.rs index cae581a..3b4752b 100644 --- a/rasn-compiler-tests/tests/edge_cases.rs +++ b/rasn-compiler-tests/tests/edge_cases.rs @@ -1,34 +1,6 @@ #![allow(non_camel_case_types)] use rasn_compiler_tests::e2e_pdu; -#[test] -fn t() { - println!( - "{:?}", - rasn_compiler::Compiler::new() - .add_asn_literal( - r#"TestModule DEFINITIONS AUTOMATIC TAGS::= BEGIN - - 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) - END"# - ) - .compile_to_string() - .unwrap() - ); -} - e2e_pdu!( distinguished_value_range, r#" Restricted ::= Distinguished (second|fourth..sixth|eighth) diff --git a/rasn-compiler-tests/tests/parse_test.rs b/rasn-compiler-tests/tests/parse_test.rs index b23e3ca..7e14d55 100644 --- a/rasn-compiler-tests/tests/parse_test.rs +++ b/rasn-compiler-tests/tests/parse_test.rs @@ -1,4 +1,4 @@ -use rasn_compiler::Compiler; +use rasn_compiler::{prelude::RasnBackend, Compiler}; #[test] #[ignore] @@ -10,7 +10,7 @@ fn parses_modules() { for entry in read_dir.flatten() { let path = entry.path(); println!("{:?}", &path); - if let Err(e) = Compiler::new() + if let Err(e) = Compiler::::new() .add_asn_by_path(path.clone()) .compile_to_string() { @@ -48,15 +48,18 @@ Failed to parse {failed} modules with the following errors: fn compile_etsi() { println!( "{:?}", - Compiler::new() - // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_class.asn") - // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_common.asn") - // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_const.asn") - // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_container.asn") - // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_ies.asn") - // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_pdus.asn") - .add_asn_by_path("../rasn-compiler/test_asn1/s1ap.asn") - .set_output_path("./tests") - .compile() + Compiler::::new_with_config(rasn_compiler::prelude::RasnConfig { + opaque_open_types: false, + default_wildcard_imports: true + }) + // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_class.asn") + // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_common.asn") + // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_const.asn") + // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_container.asn") + // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_ies.asn") + // .add_asn_by_path("../rasn-compiler/test_asn1/ngap_pdus.asn") + .add_asn_by_path("../rasn-compiler/test_asn1/Simple.asn") + .set_output_path("./tests") + .compile() ); } diff --git a/rasn-compiler/src/generator/mod.rs b/rasn-compiler/src/generator/mod.rs index 7df9dca..90dc0db 100644 --- a/rasn-compiler/src/generator/mod.rs +++ b/rasn-compiler/src/generator/mod.rs @@ -2,7 +2,9 @@ //! decoding and encoding of the parsed and validated ASN1 data elements. //! The `generator` uses string templates for generating rust code. -use std::error::Error; +use std::{error::Error, fmt::Debug}; + +use proc_macro2::TokenStream; use crate::intermediate::ToplevelDefinition; @@ -14,7 +16,8 @@ pub mod rasn; /// Implementors of the `Backend` trait can be used /// as a backend to the compiler in order to create bindings /// for other frameworks and languages than the default backend. -pub trait Backend: Sized { +pub trait Backend: Sized + Default { + type Config: Sized + Default + Debug; /// generates bindings for an ASN.1 module /// ### Params /// - `top_level_declarations` vector of [TopLevelDeclaration]s that are defined in the ASN.1 module @@ -23,9 +26,22 @@ pub trait Backend: Sized { top_level_declarations: Vec, ) -> Result; - fn format_bindings(bindings: &String) -> Result> { - Ok(bindings.clone()) + /// generates bindings for a single ASN.1 item + /// ### Params + /// - `tld` [TopLevelDeclaration] for which the bindings should be generated + fn generate(&self, tld: ToplevelDefinition) -> Result; + + /// Formats the bindings using the language- or framework-specific linters. + /// For example, the Rust backend uses rustfmt for formatting bindings. + fn format_bindings(bindings: &str) -> Result> { + Ok(bindings.to_owned()) } + + /// Returns a reference to the backend's config + fn config(&self) -> &Self::Config; + + /// Creates a backend from its config + fn from_config(config: Self::Config) -> Self; } pub struct GeneratedModule { diff --git a/rasn-compiler/src/generator/rasn/builder.rs b/rasn-compiler/src/generator/rasn/builder.rs index 929fc7a..38a20d9 100644 --- a/rasn-compiler/src/generator/rasn/builder.rs +++ b/rasn-compiler/src/generator/rasn/builder.rs @@ -1,17 +1,9 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens, TokenStreamExt}; -use std::{ - collections::BTreeMap, - env::{self}, - error::Error, - io::{self, Write}, - path::PathBuf, - process::{Command, Stdio}, - str::FromStr, -}; +use std::collections::BTreeMap; use crate::{ - generator::{Backend, GeneratedModule}, + generator::Backend, intermediate::{ constraints::Constraint, information_object::{ @@ -24,768 +16,732 @@ use crate::{ }; use super::{ - generate, information_object::InformationObjectClassField, template::*, utils::*, Rust, - BMP_STRING, GENERAL_STRING, IA5_STRING, NUMERIC_STRING, OBJECT_IDENTIFIER, PRINTABLE_STRING, - SEQUENCE_OF, SET_OF, UTF8_STRING, VISIBLE_STRING, + information_object::InformationObjectClassField, template::*, utils::*, Rasn, BMP_STRING, + GENERAL_STRING, IA5_STRING, NUMERIC_STRING, OBJECT_IDENTIFIER, PRINTABLE_STRING, SEQUENCE_OF, + SET_OF, UTF8_STRING, VISIBLE_STRING, }; use crate::generator::error::{GeneratorError, GeneratorErrorType}; pub(crate) const INNER_ARRAY_LIKE_PREFIX: &str = "Anonymous_"; -impl Backend for Rust { - fn generate_module( - &self, - tlds: Vec, - ) -> Result { - if let Some((module_ref, _)) = tlds.first().and_then(|tld| tld.get_index().cloned()) { - let module = module_ref.borrow(); - let name = to_rust_snake_case(&module.name); - let imports = module.imports.iter().map(|import| { - let module = to_rust_snake_case(&import.global_module_reference.module_reference); - let mut usages = Some(vec![]); - 'imports: for usage in &import.types { - if usage.contains("{}") || usage.chars().all(|c| c.is_uppercase() || c == '-') { - usages = None; - break 'imports; - } else if usage.starts_with(|c: char| c.is_lowercase()) { - if let Some(us) = usages.as_mut() { - us.push(to_rust_const_case(usage).to_token_stream()) - } - } else if usage.starts_with(|c: char| c.is_uppercase()) { - if let Some(us) = usages.as_mut() { - us.push(to_rust_title_case(usage).to_token_stream()) - } - } - } - let used_imports = usages.unwrap_or(vec![TokenStream::from_str("*").unwrap()]); - quote!(use super:: #module::{ #(#used_imports),* };) - }); - let (pdus, warnings): (Vec, Vec>) = - tlds.into_iter() - .fold((vec![], vec![]), |mut acc, tld| match generate(tld) { - Ok(s) => { - acc.0.push(s); - acc - } - Err(e) => { - acc.1.push(Box::new(e)); - acc - } - }); - Ok(GeneratedModule { - generated: Some(quote! { - #[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, unused)] - pub mod #name { - extern crate alloc; - - use core::borrow::Borrow; - use rasn::prelude::*; - use lazy_static::lazy_static; +macro_rules! call_template { + ($this:ident, $fn:ident, $tld:ident, $($args:expr),*) => { + Ok($fn( + $this.format_comments(&$tld.comments)?, + $this.to_rust_const_case(&$tld.name), + $($args),* + )) + }; +} - #(#imports)* +macro_rules! assignment { + ($this:ident, $unformatted:expr, $inner:expr) => {{ + let ty = $this.to_rust_title_case($unformatted); + let inner = $inner; + quote!(#ty(#inner)) + }}; +} - #(#pdus)* - } - }.to_string()), warnings}) +impl Rasn { + pub(crate) fn generate_typealias( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::ElsewhereDeclaredType(dec) = &tld.ty { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![ + quote!(delegate), + self.format_tag(tld.tag.as_ref(), false), + self.format_range_annotations(true, &dec.constraints)?, + ]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(typealias_template( + self.format_comments(&tld.comments)?, + name, + self.to_rust_title_case(&dec.identifier), + self.join_annotations(annotations), + )) } else { - Ok(GeneratedModule::empty()) + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected type alias top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } } - fn format_bindings(bindings: &String) -> Result> { - let mut rustfmt = PathBuf::from(env::var("CARGO_HOME")?); - rustfmt.push("bin/rustfmt"); - let mut cmd = Command::new(&*rustfmt); - - cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); - - let mut child = cmd.spawn()?; - let mut child_stdin = child.stdin.take().unwrap(); - let mut child_stdout = child.stdout.take().unwrap(); - - // Write to stdin in a new thread, so that we can read from stdout on this - // thread. This keeps the child from blocking on writing to its stdout which - // might block us from writing to its stdin. - let bindings = bindings.to_owned(); - let stdin_handle = ::std::thread::spawn(move || { - let _ = child_stdin.write_all(bindings.as_bytes()); - bindings - }); - - let mut output = vec![]; - io::copy(&mut child_stdout, &mut output)?; - - let status = child.wait()?; - let bindings = stdin_handle.join().expect( - "The thread writing to rustfmt's stdin doesn't do \ - anything that could panic", - ); - - match String::from_utf8(output) { - Ok(bindings) => match status.code() { - Some(0) => Ok(bindings), - Some(2) => Err(Box::new(io::Error::new( - io::ErrorKind::Other, - "Rustfmt parsing errors.".to_string(), - ))), - Some(3) => Ok(bindings), - _ => Err(Box::new(io::Error::new( - io::ErrorKind::Other, - "Internal rustfmt error".to_string(), - ))), - }, - _ => Ok(bindings), + pub(crate) fn generate_integer_value( + &self, + tld: ToplevelValueDefinition, + ) -> Result { + if let ASN1Value::LinkedIntValue { integer_type, .. } = tld.value { + let formatted_value = self.value_to_tokens(&tld.value, None)?; + let ty = self.to_rust_title_case(&tld.associated_type); + if tld.associated_type == INTEGER { + Ok(lazy_static_value_template( + self.format_comments(&tld.comments)?, + self.to_rust_const_case(&tld.name), + quote!(Integer), + formatted_value, + )) + } else if integer_type.is_unbounded() { + Ok(lazy_static_value_template( + self.format_comments(&tld.comments)?, + self.to_rust_const_case(&tld.name), + ty.clone(), + quote!(#ty(#formatted_value)), + )) + } else { + Ok(integer_value_template( + self.format_comments(&tld.comments)?, + self.to_rust_const_case(&tld.name), + ty.clone(), + quote!(#ty(#formatted_value)), + )) + } + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Value(tld)), + "Expected INTEGER value top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } } -} -pub fn generate_typealias(tld: ToplevelTypeDefinition) -> Result { - if let ASN1Type::ElsewhereDeclaredType(dec) = &tld.ty { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![ - quote!(delegate), - format_tag(tld.tag.as_ref(), false), - format_range_annotations(true, &dec.constraints)?, - ]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + pub(crate) fn generate_integer( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::Integer(ref int) = tld.ty { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![ + quote!(delegate), + self.format_range_annotations(true, &int.constraints)?, + self.format_tag(tld.tag.as_ref(), false), + ]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(integer_template( + self.format_comments(&tld.comments)?, + name, + self.join_annotations(annotations), + int.int_type().to_token_stream(), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected INTEGER top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } - Ok(typealias_template( - format_comments(&tld.comments)?, - name, - to_rust_title_case(&dec.identifier), - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected type alias top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) } -} -pub fn generate_integer_value(tld: ToplevelValueDefinition) -> Result { - if let ASN1Value::LinkedIntValue { integer_type, .. } = tld.value { - let formatted_value = value_to_tokens(&tld.value, None)?; - let ty = to_rust_title_case(&tld.associated_type); - if tld.associated_type == INTEGER { - Ok(lazy_static_value_template( - format_comments(&tld.comments)?, - to_rust_const_case(&tld.name), - quote!(Integer), - formatted_value, - )) - } else if integer_type.is_unbounded() { - Ok(lazy_static_value_template( - format_comments(&tld.comments)?, - to_rust_const_case(&tld.name), - ty.clone(), - quote!(#ty(#formatted_value)), + pub(crate) fn generate_bit_string( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::BitString(ref bitstr) = tld.ty { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![ + quote!(delegate), + self.format_range_annotations(true, &bitstr.constraints)?, + self.format_tag(tld.tag.as_ref(), false), + ]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(bit_string_template( + self.format_comments(&tld.comments)?, + name, + self.join_annotations(annotations), )) } else { - Ok(integer_value_template( - format_comments(&tld.comments)?, - to_rust_const_case(&tld.name), - ty.clone(), - quote!(#ty(#formatted_value)), + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected BIT STRING top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, )) } - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Value(tld)), - "Expected INTEGER value top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) } -} -pub fn generate_integer(tld: ToplevelTypeDefinition) -> Result { - if let ASN1Type::Integer(ref int) = tld.ty { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![ - quote!(delegate), - format_range_annotations(true, &int.constraints)?, - format_tag(tld.tag.as_ref(), false), - ]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + pub(crate) fn generate_octet_string( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::OctetString(ref oct_str) = tld.ty { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![ + quote!(delegate), + self.format_range_annotations(true, &oct_str.constraints)?, + self.format_tag(tld.tag.as_ref(), false), + ]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(octet_string_template( + self.format_comments(&tld.comments)?, + name, + self.join_annotations(annotations), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected OCTET STRING top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } - Ok(integer_template( - format_comments(&tld.comments)?, - name, - join_annotations(annotations), - int.int_type().to_token_stream(), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected INTEGER top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) } -} -pub fn generate_bit_string(tld: ToplevelTypeDefinition) -> Result { - if let ASN1Type::BitString(ref bitstr) = tld.ty { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![ - quote!(delegate), - format_range_annotations(true, &bitstr.constraints)?, - format_tag(tld.tag.as_ref(), false), - ]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + pub(crate) fn generate_character_string( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::CharacterString(ref char_str) = tld.ty { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![ + quote!(delegate), + self.format_range_annotations(true, &char_str.constraints)?, + self.format_alphabet_annotations(char_str.ty, &char_str.constraints)?, + self.format_tag(tld.tag.as_ref(), false), + ]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(char_string_template( + self.format_comments(&tld.comments)?, + name, + self.string_type(&char_str.ty)?, + self.join_annotations(annotations), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected Character String top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } - Ok(bit_string_template( - format_comments(&tld.comments)?, - name, - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected BIT STRING top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) } -} -pub fn generate_octet_string(tld: ToplevelTypeDefinition) -> Result { - if let ASN1Type::OctetString(ref oct_str) = tld.ty { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![ - quote!(delegate), - format_range_annotations(true, &oct_str.constraints)?, - format_tag(tld.tag.as_ref(), false), - ]; + pub(crate) fn generate_boolean( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + // TODO: process boolean constraints + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![quote!(delegate), self.format_tag(tld.tag.as_ref(), false)]; if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + annotations.push(self.format_identifier_annotation(&tld.name, &tld.comments, &tld.ty)); } - Ok(octet_string_template( - format_comments(&tld.comments)?, - name, - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected OCTET STRING top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) - } -} - -pub fn generate_character_string( - tld: ToplevelTypeDefinition, -) -> Result { - if let ASN1Type::CharacterString(ref char_str) = tld.ty { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![ - quote!(delegate), - format_range_annotations(true, &char_str.constraints)?, - format_alphabet_annotations(char_str.ty, &char_str.constraints)?, - format_tag(tld.tag.as_ref(), false), - ]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + if let ASN1Type::Boolean(_) = tld.ty { + Ok(boolean_template( + self.format_comments(&tld.comments)?, + name, + self.join_annotations(annotations), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected BOOLEAN top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } - Ok(char_string_template( - format_comments(&tld.comments)?, - name, - string_type(&char_str.ty)?, - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected Character String top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) - } -} - -pub fn generate_boolean(tld: ToplevelTypeDefinition) -> Result { - // TODO: process boolean constraints - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![quote!(delegate), format_tag(tld.tag.as_ref(), false)]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); } - if let ASN1Type::Boolean(_) = tld.ty { - Ok(boolean_template( - format_comments(&tld.comments)?, - name, - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected BOOLEAN top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) - } -} -macro_rules! call_template { - ($fn:ident, $tld:ident, $($args:expr),*) => { - Ok($fn( - format_comments(&$tld.comments)?, - to_rust_const_case(&$tld.name), - $($args),* - )) - }; -} - -macro_rules! assignment { - ($unformatted:expr, $inner:expr) => {{ - let ty = to_rust_title_case($unformatted); - let inner = $inner; - quote!(#ty(#inner)) - }}; -} - -pub fn generate_value(tld: ToplevelValueDefinition) -> Result { - let ty = tld.associated_type.as_str(); - match &tld.value { - ASN1Value::Null if ty == NULL => { - call_template!(primitive_value_template, tld, quote!(()), quote!(())) - } - ASN1Value::Null => call_template!( - primitive_value_template, - tld, - to_rust_title_case(&tld.associated_type), - assignment!(&tld.associated_type, quote!(())) - ), - ASN1Value::Boolean(b) if ty == BOOLEAN => call_template!( - primitive_value_template, - tld, - quote!(bool), - b.to_token_stream() - ), - ASN1Value::Boolean(b) => call_template!( - primitive_value_template, - tld, - to_rust_title_case(&tld.associated_type), - assignment!(&tld.associated_type, b.to_token_stream()) - ), - ASN1Value::LinkedIntValue { .. } => generate_integer_value(tld), - ASN1Value::BitString(_) if ty == BIT_STRING => call_template!( - lazy_static_value_template, - tld, - quote!(BitString), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::OctetString(_) if ty == OCTET_STRING => call_template!( - lazy_static_value_template, - tld, - quote!(OctetString), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::Choice { - variant_name, - inner_value, - .. - } => { - if inner_value.is_const_type() { - call_template!( - const_choice_value_template, - tld, - to_rust_title_case(&tld.associated_type), - to_rust_enum_identifier(variant_name), - value_to_tokens(inner_value, None)? - ) - } else { + pub(crate) fn generate_value( + &self, + tld: ToplevelValueDefinition, + ) -> Result { + let ty = tld.associated_type.as_str(); + match &tld.value { + ASN1Value::Null if ty == NULL => { + call_template!(self, primitive_value_template, tld, quote!(()), quote!(())) + } + ASN1Value::Null => call_template!( + self, + primitive_value_template, + tld, + self.to_rust_title_case(&tld.associated_type), + assignment!(self, &tld.associated_type, quote!(())) + ), + ASN1Value::Boolean(b) if ty == BOOLEAN => call_template!( + self, + primitive_value_template, + tld, + quote!(bool), + b.to_token_stream() + ), + ASN1Value::Boolean(b) => call_template!( + self, + primitive_value_template, + tld, + self.to_rust_title_case(&tld.associated_type), + assignment!(self, &tld.associated_type, b.to_token_stream()) + ), + ASN1Value::LinkedIntValue { .. } => self.generate_integer_value(tld), + ASN1Value::BitString(_) if ty == BIT_STRING => call_template!( + self, + lazy_static_value_template, + tld, + quote!(BitString), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::OctetString(_) if ty == OCTET_STRING => call_template!( + self, + lazy_static_value_template, + tld, + quote!(OctetString), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::Choice { + variant_name, + inner_value, + .. + } => { + if inner_value.is_const_type() { + call_template!( + self, + const_choice_value_template, + tld, + self.to_rust_title_case(&tld.associated_type), + self.to_rust_enum_identifier(variant_name), + self.value_to_tokens(inner_value, None)? + ) + } else { + call_template!( + self, + choice_value_template, + tld, + self.to_rust_title_case(&tld.associated_type), + self.to_rust_enum_identifier(variant_name), + self.value_to_tokens(inner_value, None)? + ) + } + } + ASN1Value::EnumeratedValue { + enumerated, + enumerable, + } => call_template!( + self, + enum_value_template, + tld, + self.to_rust_title_case(enumerated), + self.to_rust_enum_identifier(enumerable) + ), + ASN1Value::Time(_) if ty == GENERALIZED_TIME => call_template!( + self, + lazy_static_value_template, + tld, + quote!(GeneralizedTime), + self.value_to_tokens(&tld.value, Some("e!(GeneralizedTime)))? + ), + ASN1Value::Time(_) if ty == UTC_TIME => call_template!( + self, + lazy_static_value_template, + tld, + quote!(UtcTime), + self.value_to_tokens(&tld.value, Some("e!(UtcTime)))? + ), + ASN1Value::LinkedStructLikeValue(s) => { + let members = s + .iter() + .map(|(_, ty, val)| { + self.value_to_tokens(val.value(), self.type_to_tokens(ty).ok().as_ref()) + }) + .collect::, _>>()?; call_template!( - choice_value_template, + self, + sequence_or_set_value_template, tld, - to_rust_title_case(&tld.associated_type), - to_rust_enum_identifier(variant_name), - value_to_tokens(inner_value, None)? + self.to_rust_title_case(&tld.associated_type), + quote!(#(#members),*) ) } - } - ASN1Value::EnumeratedValue { - enumerated, - enumerable, - } => call_template!( - enum_value_template, - tld, - to_rust_title_case(enumerated), - to_rust_enum_identifier(enumerable) - ), - ASN1Value::Time(_) if ty == GENERALIZED_TIME => call_template!( - lazy_static_value_template, - tld, - quote!(GeneralizedTime), - value_to_tokens(&tld.value, Some("e!(GeneralizedTime)))? - ), - ASN1Value::Time(_) if ty == UTC_TIME => call_template!( - lazy_static_value_template, - tld, - quote!(UtcTime), - value_to_tokens(&tld.value, Some("e!(UtcTime)))? - ), - ASN1Value::LinkedStructLikeValue(s) => { - let members = s - .iter() - .map(|(_, ty, val)| value_to_tokens(val.value(), type_to_tokens(ty).ok().as_ref())) - .collect::, _>>()?; - call_template!( - sequence_or_set_value_template, + ASN1Value::LinkedNestedValue { supertypes, value } => { + let parent = supertypes.last().map(|s| self.to_rust_title_case(s)); + if value.is_const_type() { + call_template!( + self, + primitive_value_template, + tld, + self.to_rust_title_case(&tld.associated_type), + assignment!( + self, + &tld.associated_type, + self.value_to_tokens(&tld.value, parent.as_ref())? + ) + ) + } else { + call_template!( + self, + lazy_static_value_template, + tld, + self.to_rust_title_case(&tld.associated_type), + assignment!( + self, + &tld.associated_type, + self.value_to_tokens(&tld.value, parent.as_ref())? + ) + ) + } + } + ASN1Value::ObjectIdentifier(_) if ty == OBJECT_IDENTIFIER => call_template!( + self, + lazy_static_value_template, tld, - to_rust_title_case(&tld.associated_type), - quote!(#(#members),*) - ) - } - ASN1Value::LinkedNestedValue { supertypes, value } => { - let parent = supertypes.last().map(|s| to_rust_title_case(s)); - if value.is_const_type() { + quote!(ObjectIdentifier), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::LinkedCharStringValue(_, _) if ty == NUMERIC_STRING => call_template!( + self, + lazy_static_value_template, + tld, + quote!(NumericString), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::LinkedCharStringValue(_, _) if ty == VISIBLE_STRING => call_template!( + self, + lazy_static_value_template, + tld, + quote!(VisibleString), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::LinkedCharStringValue(_, _) if ty == IA5_STRING => call_template!( + self, + lazy_static_value_template, + tld, + quote!(IA5String), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::LinkedCharStringValue(_, _) if ty == UTF8_STRING => call_template!( + self, + lazy_static_value_template, + tld, + quote!(UTF8String), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::LinkedCharStringValue(_, _) if ty == BMP_STRING => call_template!( + self, + lazy_static_value_template, + tld, + quote!(BMPString), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::LinkedCharStringValue(_, _) if ty == PRINTABLE_STRING => call_template!( + self, + lazy_static_value_template, + tld, + quote!(PrintableString), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::LinkedCharStringValue(_, _) if ty == GENERAL_STRING => call_template!( + self, + lazy_static_value_template, + tld, + quote!(GeneralString), + self.value_to_tokens(&tld.value, None)? + ), + ASN1Value::LinkedArrayLikeValue(s) if ty.contains(SEQUENCE_OF) => { + let item_type = self.format_sequence_or_set_of_item_type( + ty.replace(SEQUENCE_OF, "").trim().to_string(), + s.first().map(|i| &**i), + ); call_template!( - primitive_value_template, + self, + lazy_static_value_template, tld, - to_rust_title_case(&tld.associated_type), - assignment!( - &tld.associated_type, - value_to_tokens(&tld.value, parent.as_ref())? - ) + quote!(Vec<#item_type>), + self.value_to_tokens(&tld.value, None)? ) - } else { + } + ASN1Value::LinkedArrayLikeValue(s) if ty.contains(SET_OF) => { + let item_type = self.format_sequence_or_set_of_item_type( + ty.replace(SET_OF, "").trim().to_string(), + s.first().map(|i| &**i), + ); call_template!( + self, lazy_static_value_template, tld, - to_rust_title_case(&tld.associated_type), - assignment!( - &tld.associated_type, - value_to_tokens(&tld.value, parent.as_ref())? - ) + quote!(Vec<#item_type>), + self.value_to_tokens(&tld.value, None)? ) } - } - ASN1Value::ObjectIdentifier(_) if ty == OBJECT_IDENTIFIER => call_template!( - lazy_static_value_template, - tld, - quote!(ObjectIdentifier), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::LinkedCharStringValue(_, _) if ty == NUMERIC_STRING => call_template!( - lazy_static_value_template, - tld, - quote!(NumericString), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::LinkedCharStringValue(_, _) if ty == VISIBLE_STRING => call_template!( - lazy_static_value_template, - tld, - quote!(VisibleString), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::LinkedCharStringValue(_, _) if ty == IA5_STRING => call_template!( - lazy_static_value_template, - tld, - quote!(IA5String), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::LinkedCharStringValue(_, _) if ty == UTF8_STRING => call_template!( - lazy_static_value_template, - tld, - quote!(UTF8String), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::LinkedCharStringValue(_, _) if ty == BMP_STRING => call_template!( - lazy_static_value_template, - tld, - quote!(BMPString), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::LinkedCharStringValue(_, _) if ty == PRINTABLE_STRING => call_template!( - lazy_static_value_template, - tld, - quote!(PrintableString), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::LinkedCharStringValue(_, _) if ty == GENERAL_STRING => call_template!( - lazy_static_value_template, - tld, - quote!(GeneralString), - value_to_tokens(&tld.value, None)? - ), - ASN1Value::LinkedArrayLikeValue(s) if ty.contains(SEQUENCE_OF) => { - let item_type = format_sequence_or_set_of_item_type( - ty.replace(SEQUENCE_OF, "").trim().to_string(), - s.first().map(|i| &**i), - ); - call_template!( - lazy_static_value_template, - tld, - quote!(Vec<#item_type>), - value_to_tokens(&tld.value, None)? - ) - } - ASN1Value::LinkedArrayLikeValue(s) if ty.contains(SET_OF) => { - let item_type = format_sequence_or_set_of_item_type( - ty.replace(SET_OF, "").trim().to_string(), - s.first().map(|i| &**i), - ); - call_template!( + ASN1Value::BitString(_) + | ASN1Value::Time(_) + | ASN1Value::LinkedCharStringValue(_, _) + | ASN1Value::ObjectIdentifier(_) + | ASN1Value::LinkedArrayLikeValue(_) + | ASN1Value::ElsewhereDeclaredValue { .. } + | ASN1Value::OctetString(_) => call_template!( + self, lazy_static_value_template, tld, - quote!(Vec<#item_type>), - value_to_tokens(&tld.value, None)? - ) + self.to_rust_title_case(&tld.associated_type), + assignment!( + self, + &tld.associated_type, + self.value_to_tokens(&tld.value, None)? + ) + ), + _ => Ok(TokenStream::new()), } - ASN1Value::BitString(_) - | ASN1Value::Time(_) - | ASN1Value::LinkedCharStringValue(_, _) - | ASN1Value::ObjectIdentifier(_) - | ASN1Value::LinkedArrayLikeValue(_) - | ASN1Value::ElsewhereDeclaredValue { .. } - | ASN1Value::OctetString(_) => call_template!( - lazy_static_value_template, - tld, - to_rust_title_case(&tld.associated_type), - assignment!(&tld.associated_type, value_to_tokens(&tld.value, None)?) - ), - _ => Ok(TokenStream::new()), } -} - -pub fn generate_any(tld: ToplevelTypeDefinition) -> Result { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![quote!(delegate), format_tag(tld.tag.as_ref(), false)]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); - } - Ok(any_template( - format_comments(&tld.comments)?, - name, - join_annotations(annotations), - )) -} -pub fn generate_generalized_time( - tld: ToplevelTypeDefinition, -) -> Result { - if let ASN1Type::GeneralizedTime(_) = &tld.ty { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![quote!(delegate), format_tag(tld.tag.as_ref(), false)]; + pub(crate) fn generate_any( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![quote!(delegate), self.format_tag(tld.tag.as_ref(), false)]; if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + annotations.push(self.format_identifier_annotation(&tld.name, &tld.comments, &tld.ty)); } - Ok(generalized_time_template( - format_comments(&tld.comments)?, + Ok(any_template( + self.format_comments(&tld.comments)?, name, - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected GeneralizedTime top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, + self.join_annotations(annotations), )) } -} -pub fn generate_utc_time(tld: ToplevelTypeDefinition) -> Result { - if let ASN1Type::UTCTime(_) = &tld.ty { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![quote!(delegate), format_tag(tld.tag.as_ref(), false)]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + pub(crate) fn generate_generalized_time( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::GeneralizedTime(_) = &tld.ty { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![quote!(delegate), self.format_tag(tld.tag.as_ref(), false)]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(generalized_time_template( + self.format_comments(&tld.comments)?, + name, + self.join_annotations(annotations), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected GeneralizedTime top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } - Ok(utc_time_template( - format_comments(&tld.comments)?, - name, - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected UTCTime top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) } -} -pub fn generate_oid(tld: ToplevelTypeDefinition) -> Result { - if let ASN1Type::ObjectIdentifier(oid) = &tld.ty { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![ - quote!(delegate), - format_tag(tld.tag.as_ref(), false), - format_range_annotations(false, &oid.constraints)?, - ]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + pub(crate) fn generate_utc_time( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::UTCTime(_) = &tld.ty { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![quote!(delegate), self.format_tag(tld.tag.as_ref(), false)]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(utc_time_template( + self.format_comments(&tld.comments)?, + name, + self.join_annotations(annotations), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected UTCTime top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } - Ok(oid_template( - format_comments(&tld.comments)?, - name, - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected OBJECT IDENTIFIER top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) } -} -pub fn generate_null(tld: ToplevelTypeDefinition) -> Result { - if let ASN1Type::Null = tld.ty { - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![quote!(delegate), format_tag(tld.tag.as_ref(), false)]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + pub(crate) fn generate_oid( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::ObjectIdentifier(oid) = &tld.ty { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![ + quote!(delegate), + self.format_tag(tld.tag.as_ref(), false), + self.format_range_annotations(false, &oid.constraints)?, + ]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(oid_template( + self.format_comments(&tld.comments)?, + name, + self.join_annotations(annotations), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected OBJECT IDENTIFIER top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } - Ok(null_template( - format_comments(&tld.comments)?, - name, - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected NULL top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) } -} -pub fn generate_enumerated(tld: ToplevelTypeDefinition) -> Result { - if let ASN1Type::Enumerated(ref enumerated) = tld.ty { - let extensible = enumerated - .extensible - .map(|_| { - quote! { - #[non_exhaustive]} - }) - .unwrap_or_default(); - let name = to_rust_title_case(&tld.name); - let mut annotations = vec![quote!(enumerated), format_tag(tld.tag.as_ref(), false)]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + pub(crate) fn generate_null( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::Null = tld.ty { + let name = self.to_rust_title_case(&tld.name); + let mut annotations = vec![quote!(delegate), self.format_tag(tld.tag.as_ref(), false)]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(null_template( + self.format_comments(&tld.comments)?, + name, + self.join_annotations(annotations), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected NULL top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } - Ok(enumerated_template( - format_comments(&tld.comments)?, - name, - extensible, - format_enum_members(enumerated), - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected ENUMERATED top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) } -} -pub fn generate_choice(tld: ToplevelTypeDefinition) -> Result { - if let ASN1Type::Choice(ref choice) = tld.ty { - let name = to_rust_title_case(&tld.name); - let inner_options = format_nested_choice_options(choice, &name.to_string())?; - let extensible = choice - .extensible - .map(|_| { - quote! { - #[non_exhaustive]} - }) - .unwrap_or_default(); - let mut annotations = vec![quote!(choice), format_tag(tld.tag.as_ref(), true)]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + pub(crate) fn generate_enumerated( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::Enumerated(ref enumerated) = tld.ty { + let extensible = enumerated + .extensible + .map(|_| { + quote! { + #[non_exhaustive]} + }) + .unwrap_or_default(); + let name = self.to_rust_title_case(&tld.name); + let mut annotations = + vec![quote!(enumerated), self.format_tag(tld.tag.as_ref(), false)]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(enumerated_template( + self.format_comments(&tld.comments)?, + name, + extensible, + self.format_enum_members(enumerated), + self.join_annotations(annotations), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected ENUMERATED top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) } - Ok(choice_template( - format_comments(&tld.comments)?, - name.clone(), - extensible, - format_choice_options(choice, &name.to_string())?, - inner_options, - join_annotations(annotations), - )) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected CHOICE top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) } -} -pub fn generate_sequence_or_set( - tld: ToplevelTypeDefinition, -) -> Result { - match tld.ty { - ASN1Type::Sequence(ref seq) | ASN1Type::Set(ref seq) => { - let name = to_rust_title_case(&tld.name); - let extensible = seq + pub(crate) fn generate_choice( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + if let ASN1Type::Choice(ref choice) = tld.ty { + let name = self.to_rust_title_case(&tld.name); + let inner_options = self.format_nested_choice_options(choice, &name.to_string())?; + let extensible = choice .extensible .map(|_| { quote! { #[non_exhaustive]} }) .unwrap_or_default(); - let set_annotation = if let ASN1Type::Set(_) = tld.ty { - quote!(set) - } else { - TokenStream::new() - }; - let class_fields = seq.members.iter().fold( + let mut annotations = vec![quote!(choice), self.format_tag(tld.tag.as_ref(), true)]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(choice_template( + self.format_comments(&tld.comments)?, + name.clone(), + extensible, + self.format_choice_options(choice, &name.to_string())?, + inner_options, + self.join_annotations(annotations), + )) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Type(tld)), + "Expected CHOICE top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) + } + } + + pub(crate) fn generate_sequence_or_set( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + match tld.ty { + ASN1Type::Sequence(ref seq) | ASN1Type::Set(ref seq) => { + let name = self.to_rust_title_case(&tld.name); + let extensible = seq + .extensible + .map(|_| { + quote! { + #[non_exhaustive]} + }) + .unwrap_or_default(); + let set_annotation = if let ASN1Type::Set(_) = tld.ty { + quote!(set) + } else { + TokenStream::new() + }; + let class_fields = if self.config.opaque_open_types { + TokenStream::new() + } else { + seq.members.iter().fold( TokenStream::new(), |mut acc, m| { [ @@ -795,290 +751,299 @@ pub fn generate_sequence_or_set( .concat() .iter() .for_each(|c| { - let decode_fn = format_ident!("decode_{}", to_rust_snake_case(&m.name)); - let open_field_name = to_rust_snake_case(&m.name); - if let (Constraint::TableConstraint(t), ASN1Type::InformationObjectFieldReference(iofr)) = (c, &m.ty) { - let identifier = t.linked_fields.iter().map(|l| - to_rust_snake_case(&l.field_name) - ); - let field_name = iofr.field_path.last().unwrap().identifier().replace('&', ""); - if field_name.starts_with(|initial: char| initial.is_lowercase()) { - // Fixed-value fields of Information Object usages should have been resolved at this point - return; - } - let obj_set_name = match t.object_set.values.first() { - Some(ObjectSetValue::Reference(s)) => to_rust_title_case(s), - _ => todo!() - }; - let field_enum_name = format_ident!("{obj_set_name}_{field_name}"); - let input = m.is_optional.then(|| quote!(self. #open_field_name .as_ref())).unwrap_or(quote!(Some(&self. #open_field_name))); - acc.append_all(quote! { - - impl #name { - pub fn #decode_fn(&self, decoder: &mut D) -> Result<#field_enum_name, D::Error> { - #field_enum_name ::decode(decoder, #input, &self. #(#identifier).*) + if let (Constraint::TableConstraint(t), ASN1Type::InformationObjectFieldReference(iofr)) = (c, &m.ty) { + let decode_fn = format_ident!("decode_{}", self.to_rust_snake_case(&m.name)); + let open_field_name = self.to_rust_snake_case(&m.name); + let identifier = t.linked_fields.iter().map(|l| + self.to_rust_snake_case(&l.field_name) + ); + let field_name = iofr.field_path.last().unwrap().identifier().replace('&', ""); + if field_name.starts_with(|initial: char| initial.is_lowercase()) { + // Fixed-value fields of Information Object usages should have been resolved at this point + return; } - } + let obj_set_name = match t.object_set.values.first() { + Some(ObjectSetValue::Reference(s)) => self.to_rust_title_case(s), + _ => todo!() + }; + let field_enum_name = format_ident!("{obj_set_name}_{field_name}"); + let input = m.is_optional.then(|| quote!(self. #open_field_name .as_ref())).unwrap_or(quote!(Some(&self. #open_field_name))); + acc.append_all(quote! { + + impl #name { + pub fn #decode_fn(&self, decoder: &mut D) -> Result<#field_enum_name, D::Error> { + #field_enum_name ::decode(decoder, #input, &self. #(#identifier).*) + } + } + }); + }; }); + acc + }) }; - }); - acc - }); - let (declaration, name_types) = format_sequence_or_set_members(seq, &name.to_string())?; - let mut annotations = vec![set_annotation, format_tag(tld.tag.as_ref(), true)]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); + let (declaration, name_types) = + self.format_sequence_or_set_members(seq, &name.to_string())?; + let mut annotations = vec![set_annotation, self.format_tag(tld.tag.as_ref(), true)]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation( + &tld.name, + &tld.comments, + &tld.ty, + )); + } + Ok(sequence_or_set_template( + self.format_comments(&tld.comments)?, + name.clone(), + extensible, + declaration, + self.format_nested_sequence_members(seq, &name.to_string())?, + self.join_annotations(annotations), + self.format_default_methods(&seq.members, &name.to_string())?, + self.format_new_impl(&name, name_types), + class_fields, + )) } - Ok(sequence_or_set_template( - format_comments(&tld.comments)?, - name.clone(), - extensible, - declaration, - format_nested_sequence_members(seq, &name.to_string())?, - join_annotations(annotations), - format_default_methods(&seq.members, &name.to_string())?, - format_new_impl(&name, name_types), - class_fields, - )) - } - _ => Err(GeneratorError::new( - Some(ToplevelDefinition::Type(tld)), - "Expected SEQUENCE top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )), - } -} - -pub fn generate_sequence_or_set_of( - tld: ToplevelTypeDefinition, -) -> Result { - let (is_set_of, seq_or_set_of) = match &tld.ty { - ASN1Type::SetOf(se_of) => (true, se_of), - ASN1Type::SequenceOf(se_of) => (false, se_of), - _ => { - return Err(GeneratorError::new( + _ => Err(GeneratorError::new( Some(ToplevelDefinition::Type(tld)), - "Expected SEQUENCE OF top-level declaration", + "Expected SEQUENCE top-level declaration", GeneratorErrorType::Asn1TypeMismatch, - )) + )), } - }; - let name = to_rust_title_case(&tld.name); - let anonymous_item = match seq_or_set_of.element_type.as_ref() { - ASN1Type::ElsewhereDeclaredType(_) => None, - n => Some(generate(ToplevelDefinition::Type( - ToplevelTypeDefinition { - parameterization: None, - comments: format!( - " Anonymous {} OF member ", - if is_set_of { "SET" } else { "SEQUENCE" } - ), - name: String::from(INNER_ARRAY_LIKE_PREFIX) + &name.to_string(), - ty: n.clone(), - tag: None, - index: None, - }, - ))?), } - .unwrap_or_default(); - let member_type = match seq_or_set_of.element_type.as_ref() { - ASN1Type::ElsewhereDeclaredType(d) => to_rust_title_case(&d.identifier), - _ => format_ident!("Anonymous{}", &name.to_string()).to_token_stream(), - }; - let mut annotations = vec![ - quote!(delegate), - format_range_annotations(true, &seq_or_set_of.constraints)?, - format_tag(tld.tag.as_ref(), false), - ]; - if name.to_string() != tld.name { - annotations.push(format_identifier_annotation( - &tld.name, - &tld.comments, - &tld.ty, - )); - } - Ok(sequence_or_set_of_template( - is_set_of, - format_comments(&tld.comments)?, - name, - anonymous_item, - member_type, - join_annotations(annotations), - )) -} -pub fn generate_information_object_set( - tld: ToplevelInformationDefinition, -) -> Result { - if let ASN1Information::ObjectSet(o) = &tld.value { - let class: &InformationObjectClass = match tld.class { - Some(ClassLink::ByReference(ref c)) => c, + pub(crate) fn generate_sequence_or_set_of( + &self, + tld: ToplevelTypeDefinition, + ) -> Result { + let (is_set_of, seq_or_set_of) = match &tld.ty { + ASN1Type::SetOf(se_of) => (true, se_of), + ASN1Type::SequenceOf(se_of) => (false, se_of), _ => { return Err(GeneratorError::new( - None, - "Missing class link in Information Object Set", - GeneratorErrorType::MissingClassKey, + Some(ToplevelDefinition::Type(tld)), + "Expected SEQUENCE OF top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, )) } }; - let mut keys_to_types = o - .values - .iter() - .map(|v| match v { - ObjectSetValue::Reference(r) => Err(GeneratorError::new( - None, - &format!("Could not resolve reference of Information Object Set {r}"), - GeneratorErrorType::MissingClassKey, - )), - ObjectSetValue::Inline(InformationObjectFields::CustomSyntax(_)) => { - Err(GeneratorError::new( - Some(ToplevelDefinition::Information(tld.clone())), - "Unexpectedly encountered unresolved custom syntax!", + let name = self.to_rust_title_case(&tld.name); + let anonymous_item = match seq_or_set_of.element_type.as_ref() { + ASN1Type::ElsewhereDeclaredType(_) => None, + n => Some( + self.generate(ToplevelDefinition::Type(ToplevelTypeDefinition { + parameterization: None, + comments: format!( + " Anonymous {} OF member ", + if is_set_of { "SET" } else { "SEQUENCE" } + ), + name: String::from(INNER_ARRAY_LIKE_PREFIX) + &name.to_string(), + ty: n.clone(), + tag: None, + index: None, + }))?, + ), + } + .unwrap_or_default(); + let member_type = match seq_or_set_of.element_type.as_ref() { + ASN1Type::ElsewhereDeclaredType(d) => self.to_rust_title_case(&d.identifier), + _ => format_ident!("Anonymous{}", &name.to_string()).to_token_stream(), + }; + let mut annotations = vec![ + quote!(delegate), + self.format_range_annotations(true, &seq_or_set_of.constraints)?, + self.format_tag(tld.tag.as_ref(), false), + ]; + if name.to_string() != tld.name { + annotations.push(self.format_identifier_annotation(&tld.name, &tld.comments, &tld.ty)); + } + Ok(sequence_or_set_of_template( + is_set_of, + self.format_comments(&tld.comments)?, + name, + anonymous_item, + member_type, + self.join_annotations(annotations), + )) + } + + pub(crate) fn generate_information_object_set( + &self, + tld: ToplevelInformationDefinition, + ) -> Result { + if self.config.opaque_open_types { + return Ok(TokenStream::new()); + } + if let ASN1Information::ObjectSet(o) = &tld.value { + let class: &InformationObjectClass = match tld.class { + Some(ClassLink::ByReference(ref c)) => c, + _ => { + return Err(GeneratorError::new( + None, + "Missing class link in Information Object Set", GeneratorErrorType::MissingClassKey, )) } - ObjectSetValue::Inline(InformationObjectFields::DefaultSyntax(s)) => { - resolve_standard_syntax(class, s) - } - }) - .collect::)>, _>>()?; - let mut choices = BTreeMap::>::new(); - for (key, items) in keys_to_types.drain(..) { - for (index, item) in items { - let id = class - .fields - .get(index) - .map(|f| f.identifier.identifier()) - .ok_or_else(|| GeneratorError { - top_level_declaration: Some(ToplevelDefinition::Information(tld.clone())), - details: "Could not find class field for index.".into(), - kind: GeneratorErrorType::SyntaxMismatch, - })?; - match choices.get_mut(id) { - Some(entry) => entry.push((key.clone(), item)), - None => { - choices.insert(id.clone(), vec![(key.clone(), item)]); + }; + let mut keys_to_types = o + .values + .iter() + .map(|v| match v { + ObjectSetValue::Reference(r) => Err(GeneratorError::new( + None, + &format!("Could not resolve reference of Information Object Set {r}"), + GeneratorErrorType::MissingClassKey, + )), + ObjectSetValue::Inline(InformationObjectFields::CustomSyntax(_)) => { + Err(GeneratorError::new( + Some(ToplevelDefinition::Information(tld.clone())), + "Unexpectedly encountered unresolved custom syntax!", + GeneratorErrorType::MissingClassKey, + )) + } + ObjectSetValue::Inline(InformationObjectFields::DefaultSyntax(s)) => { + self.resolve_standard_syntax(class, s) + } + }) + .collect::)>, _>>()?; + let mut choices = BTreeMap::>::new(); + for (key, items) in keys_to_types.drain(..) { + for (index, item) in items { + let id = class + .fields + .get(index) + .map(|f| f.identifier.identifier()) + .ok_or_else(|| GeneratorError { + top_level_declaration: Some(ToplevelDefinition::Information( + tld.clone(), + )), + details: "Could not find class field for index.".into(), + kind: GeneratorErrorType::SyntaxMismatch, + })?; + match choices.get_mut(id) { + Some(entry) => entry.push((key.clone(), item)), + None => { + choices.insert(id.clone(), vec![(key.clone(), item)]); + } } } } - } - if choices.is_empty() { - for InformationObjectClassField { identifier, .. } in &class.fields { - choices.insert(identifier.identifier().clone(), Vec::new()); + if choices.is_empty() { + for InformationObjectClassField { identifier, .. } in &class.fields { + choices.insert(identifier.identifier().clone(), Vec::new()); + } } - } - let name = to_rust_title_case(&tld.name); - let class_unique_id_type = class - .fields - .iter() - .find_map(|f| (f.is_unique).then(|| f.ty.clone())) - .flatten() - .ok_or_else(|| GeneratorError { - top_level_declaration: None, - details: "Could not determine unique class identifier type.".into(), - kind: GeneratorErrorType::SyntaxMismatch, - })?; - let class_unique_id_type_name = type_to_tokens(&class_unique_id_type)?; + let name = self.to_rust_title_case(&tld.name); + let class_unique_id_type = class + .fields + .iter() + .find_map(|f| (f.is_unique).then(|| f.ty.clone())) + .flatten() + .ok_or_else(|| GeneratorError { + top_level_declaration: None, + details: "Could not determine unique class identifier type.".into(), + kind: GeneratorErrorType::SyntaxMismatch, + })?; + let class_unique_id_type_name = self.type_to_tokens(&class_unique_id_type)?; - let mut field_enums = vec![]; - for (field_name, fields) in choices.iter() { - let field_enum_name = format_ident!("{name}_{}", field_name.replace('&', "")); - let (mut ids, mut inner_types) = (vec![], vec![]); - for (index, (id, ty)) in fields.iter().enumerate() { - let identifier_value = match id { - ASN1Value::LinkedElsewhereDefinedValue { - can_be_const: false, - .. - } => { - let tokenized_value = - value_to_tokens(id, Some(&class_unique_id_type_name))?; - quote!(*#tokenized_value) - } - ASN1Value::LinkedNestedValue { value, .. } - if matches![ - &**value, - ASN1Value::LinkedElsewhereDefinedValue { - can_be_const: false, - .. - } - ] => - { - let tokenized_value = - value_to_tokens(value, Some(&class_unique_id_type_name))?; - quote!(*#tokenized_value) - } - ASN1Value::LinkedNestedValue { value, .. } - if matches![&**value, ASN1Value::LinkedElsewhereDefinedValue { .. }] => - { - value_to_tokens(value, Some(&class_unique_id_type_name))? - } - _ => value_to_tokens(id, Some(&class_unique_id_type_name))?, - }; - let type_id = type_to_tokens(ty).unwrap_or(quote!(Option<()>)); - let variant_name = match id { - ASN1Value::LinkedElsewhereDefinedValue { - identifier: ref_id, .. - } - | ASN1Value::ElsewhereDeclaredValue { - identifier: ref_id, .. - } => to_rust_title_case(ref_id), - _ => format_ident!("{field_enum_name}_{index}").to_token_stream(), - }; - if ty.constraints().map_or(true, |c| c.is_empty()) { - ids.push((variant_name, type_id, identifier_value)); - inner_types.push(TokenStream::new()); - } else { - let (signed_range, character_string_type) = match ty { - ASN1Type::CharacterString(c) => (false, Some(c.ty)), - ASN1Type::Integer(_) => (true, None), - ASN1Type::Real(_) => (true, None), - ASN1Type::BitString(_) => (false, None), - ASN1Type::OctetString(_) => (false, None), - _ => (false, None), + let mut field_enums = vec![]; + for (field_name, fields) in choices.iter() { + let field_enum_name = format_ident!("{name}_{}", field_name.replace('&', "")); + let (mut ids, mut inner_types) = (vec![], vec![]); + for (index, (id, ty)) in fields.iter().enumerate() { + let identifier_value = match id { + ASN1Value::LinkedElsewhereDefinedValue { + can_be_const: false, + .. + } => { + let tokenized_value = + self.value_to_tokens(id, Some(&class_unique_id_type_name))?; + quote!(*#tokenized_value) + } + ASN1Value::LinkedNestedValue { value, .. } + if matches![ + &**value, + ASN1Value::LinkedElsewhereDefinedValue { + can_be_const: false, + .. + } + ] => + { + let tokenized_value = + self.value_to_tokens(value, Some(&class_unique_id_type_name))?; + quote!(*#tokenized_value) + } + ASN1Value::LinkedNestedValue { value, .. } + if matches![ + &**value, + ASN1Value::LinkedElsewhereDefinedValue { .. } + ] => + { + self.value_to_tokens(value, Some(&class_unique_id_type_name))? + } + _ => self.value_to_tokens(id, Some(&class_unique_id_type_name))?, }; - let delegate_id = &format!("Inner_{field_enum_name}_{index}") - .parse::() - .unwrap(); - let range_constraints = format_range_annotations( - signed_range, - ty.constraints().unwrap_or(&Vec::<_>::new()), - ) - .unwrap(); - let alphabet_constraints = character_string_type - .and_then(|c| { - format_alphabet_annotations( - c, + let type_id = self.type_to_tokens(ty).unwrap_or(quote!(Option<()>)); + let variant_name = match id { + ASN1Value::LinkedElsewhereDefinedValue { + identifier: ref_id, .. + } + | ASN1Value::ElsewhereDeclaredValue { + identifier: ref_id, .. + } => self.to_rust_title_case(ref_id), + _ => format_ident!("{field_enum_name}_{index}").to_token_stream(), + }; + if ty.constraints().map_or(true, |c| c.is_empty()) { + ids.push((variant_name, type_id, identifier_value)); + inner_types.push(TokenStream::new()); + } else { + let (signed_range, character_string_type) = match ty { + ASN1Type::CharacterString(c) => (false, Some(c.ty)), + ASN1Type::Integer(_) => (true, None), + ASN1Type::Real(_) => (true, None), + ASN1Type::BitString(_) => (false, None), + ASN1Type::OctetString(_) => (false, None), + _ => (false, None), + }; + let delegate_id = &format!("Inner_{field_enum_name}_{index}") + .parse::() + .unwrap(); + let range_constraints = self + .format_range_annotations( + signed_range, ty.constraints().unwrap_or(&Vec::<_>::new()), ) - .ok() - }) - .unwrap_or_default(); - let annotations = join_annotations(vec![ - range_constraints, - alphabet_constraints, - quote!(delegate), - ]); - ids.push((variant_name, delegate_id.clone(), identifier_value)); - inner_types.push(quote! { - #[derive(Debug, Clone, PartialEq, AsnType, Decode, Encode)] - #annotations - pub struct #delegate_id (pub #type_id); + .unwrap(); + let alphabet_constraints = character_string_type + .and_then(|c| { + self.format_alphabet_annotations( + c, + ty.constraints().unwrap_or(&Vec::<_>::new()), + ) + .ok() + }) + .unwrap_or_default(); + let annotations = self.join_annotations(vec![ + range_constraints, + alphabet_constraints, + quote!(delegate), + ]); + ids.push((variant_name, delegate_id.clone(), identifier_value)); + inner_types.push(quote! { + #[derive(Debug, Clone, PartialEq, AsnType, Decode, Encode)] + #annotations + pub struct #delegate_id (pub #type_id); - }); + }); + } } - } - let variants = ids - .iter() - .map(|(variant_name, type_id, _)| quote!(#variant_name (#type_id),)); + let variants = ids + .iter() + .map(|(variant_name, type_id, _)| quote!(#variant_name (#type_id),)); - let de_match_arms = ids.iter().map(|(variant_name, _, identifier_value)| { + let de_match_arms = ids.iter().map(|(variant_name, _, identifier_value)| { quote!(i if i == &#identifier_value => Ok(decoder.codec().decode_from_binary(open_type_payload.ok_or_else(|| rasn::error::DecodeError::from_kind( rasn::error::DecodeErrorKind::Custom { msg: "Failed to decode open type! No input data given.".into(), @@ -1087,11 +1052,11 @@ pub fn generate_information_object_set( ).into())?.as_bytes()).map(Self:: #variant_name)?),) }); - let en_match_arms = ids.iter().map(|(variant_name, _, identifier_value)| { + let en_match_arms = ids.iter().map(|(variant_name, _, identifier_value)| { quote!((Self::#variant_name (inner), i) if i == &#identifier_value =>inner.encode(encoder),) }); - field_enums.push(quote! { + field_enums.push(quote! { #(#inner_types)* #[derive(Debug, Clone, PartialEq)] @@ -1126,14 +1091,15 @@ pub fn generate_information_object_set( } }); - } + } - Ok(quote!(#(#field_enums)*)) - } else { - Err(GeneratorError::new( - Some(ToplevelDefinition::Information(tld)), - "Expected Object Set top-level declaration", - GeneratorErrorType::Asn1TypeMismatch, - )) + Ok(quote!(#(#field_enums)*)) + } else { + Err(GeneratorError::new( + Some(ToplevelDefinition::Information(tld)), + "Expected Object Set top-level declaration", + GeneratorErrorType::Asn1TypeMismatch, + )) + } } } diff --git a/rasn-compiler/src/generator/rasn/mod.rs b/rasn-compiler/src/generator/rasn/mod.rs index e680bbf..f1c8e08 100644 --- a/rasn-compiler/src/generator/rasn/mod.rs +++ b/rasn-compiler/src/generator/rasn/mod.rs @@ -1,57 +1,219 @@ -use self::{builder::*, information_object::ASN1Information}; +use std::{ + env, + error::Error, + io::{self, Write}, + path::PathBuf, + process::{Command, Stdio}, + str::FromStr, +}; + +use self::information_object::ASN1Information; use crate::intermediate::*; use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; -use super::error::{GeneratorError, GeneratorErrorType}; +use super::{ + error::{GeneratorError, GeneratorErrorType}, + Backend, GeneratedModule, +}; mod builder; mod template; mod utils; -pub struct Rust; +#[derive(Debug, Default)] +/// A compiler backend that generates bindings to be used with +/// the `rasn` framework for rust. +pub struct Rasn { + config: Config, +} -fn generate(tld: ToplevelDefinition) -> Result { - match tld { - ToplevelDefinition::Type(t) => { - if t.parameterization.is_some() { - return Ok(TokenStream::new()); - } - match t.ty { - ASN1Type::Null => generate_null(t), - ASN1Type::Boolean(_) => generate_boolean(t), - ASN1Type::Integer(_) => generate_integer(t), - ASN1Type::Enumerated(_) => generate_enumerated(t), - ASN1Type::BitString(_) => generate_bit_string(t), - ASN1Type::CharacterString(_) => generate_character_string(t), - ASN1Type::Sequence(_) | ASN1Type::Set(_) => generate_sequence_or_set(t), - ASN1Type::SequenceOf(_) | ASN1Type::SetOf(_) => generate_sequence_or_set_of(t), - ASN1Type::ElsewhereDeclaredType(_) => generate_typealias(t), - ASN1Type::Choice(_) => generate_choice(t), - ASN1Type::OctetString(_) => generate_octet_string(t), - ASN1Type::Time(_) => unimplemented!("rasn does not support TIME types yet!"), - ASN1Type::Real(_) => Err(GeneratorError { - kind: GeneratorErrorType::NotYetInplemented, - details: "Real types are currently unsupported!".into(), - top_level_declaration: None, - }), - ASN1Type::ObjectIdentifier(_) => generate_oid(t), - ASN1Type::InformationObjectFieldReference(_) - | ASN1Type::EmbeddedPdv - | ASN1Type::External => generate_any(t), - ASN1Type::GeneralizedTime(_) => generate_generalized_time(t), - ASN1Type::UTCTime(_) => generate_utc_time(t), - ASN1Type::ChoiceSelectionType(_) => Err(GeneratorError { - kind: GeneratorErrorType::Asn1TypeMismatch, - details: "Choice selection type should have been resolved at this point!" - .into(), - top_level_declaration: None, - }), +#[derive(Debug, Default)] +/// A configuration for the [Rasn] backend +pub struct Config { + /// ASN.1 Open Types are represented as the `rasn::types::Any` type, + /// which holds a binary `content`. If `opaque_open_types` is `false`, + /// the compiler will generate de-/encode methods for all rust types + /// that hold an open type. In this way, for example a SEQUENCE field + /// of an Open Type can be completely decoded to a rust type instance. + /// While with `opaque_open_type == true`, the same SEQUENCE field would + /// be represented as an `Any`-wrapped `Vec` containing the encoded + /// actual value of the Open Type. + pub opaque_open_types: bool, + /// The compiler will try to match module import dependencies of the ASN.1 + /// module as close as possible, importing only those types from other modules + /// that are imported in the ASN.1 module. If the `default_wildcard_imports` + /// 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, +} + +impl Backend for Rasn { + type Config = Config; + + fn from_config(config: Self::Config) -> Self { + Self { config } + } + + fn config(&self) -> &Self::Config { + &self.config + } + + fn generate_module( + &self, + tlds: Vec, + ) -> Result { + if let Some((module_ref, _)) = tlds.first().and_then(|tld| tld.get_index().cloned()) { + let module = module_ref.borrow(); + let name = self.to_rust_snake_case(&module.name); + let imports = module.imports.iter().map(|import| { + let module = + self.to_rust_snake_case(&import.global_module_reference.module_reference); + let mut usages = Some(vec![]); + 'imports: for usage in &import.types { + if usage.contains("{}") || usage.chars().all(|c| c.is_uppercase() || c == '-') { + usages = None; + break 'imports; + } else if usage.starts_with(|c: char| c.is_lowercase()) { + if let Some(us) = usages.as_mut() { + us.push(self.to_rust_const_case(usage).to_token_stream()) + } + } else if usage.starts_with(|c: char| c.is_uppercase()) { + if let Some(us) = usages.as_mut() { + us.push(self.to_rust_title_case(usage).to_token_stream()) + } + } + } + let used_imports = if self.config.default_wildcard_imports { + vec![TokenStream::from_str("*").unwrap()] + } else { + usages.unwrap_or(vec![TokenStream::from_str("*").unwrap()]) + }; + quote!(use super:: #module::{ #(#used_imports),* };) + }); + let (pdus, warnings): (Vec, Vec>) = + tlds.into_iter() + .fold((vec![], vec![]), |mut acc, tld| match self.generate(tld) { + Ok(s) => { + acc.0.push(s); + acc + } + Err(e) => { + acc.1.push(Box::new(e)); + acc + } + }); + Ok(GeneratedModule { + generated: Some(quote! { + #[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, unused)] + pub mod #name { + extern crate alloc; + + use core::borrow::Borrow; + use rasn::prelude::*; + use lazy_static::lazy_static; + + #(#imports)* + + #(#pdus)* + } + }.to_string()), warnings}) + } else { + Ok(GeneratedModule::empty()) + } + } + + fn format_bindings(bindings: &str) -> Result> { + let mut rustfmt = PathBuf::from(env::var("CARGO_HOME")?); + rustfmt.push("bin/rustfmt"); + let mut cmd = Command::new(&*rustfmt); + + cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); + + let mut child = cmd.spawn()?; + let mut child_stdin = child.stdin.take().unwrap(); + let mut child_stdout = child.stdout.take().unwrap(); + + // Write to stdin in a new thread, so that we can read from stdout on this + // thread. This keeps the child from blocking on writing to its stdout which + // might block us from writing to its stdin. + let bindings = bindings.to_owned(); + let stdin_handle = ::std::thread::spawn(move || { + let _ = child_stdin.write_all(bindings.as_bytes()); + bindings + }); + + let mut output = vec![]; + io::copy(&mut child_stdout, &mut output)?; + + let status = child.wait()?; + let bindings = stdin_handle.join().expect( + "The thread writing to rustfmt's stdin doesn't do \ + anything that could panic", + ); + + match String::from_utf8(output) { + Ok(bindings) => match status.code() { + Some(0) => Ok(bindings), + Some(2) => Err(Box::new(io::Error::new( + io::ErrorKind::Other, + "Rustfmt parsing errors.".to_string(), + ))), + Some(3) => Ok(bindings), + _ => Err(Box::new(io::Error::new( + io::ErrorKind::Other, + "Internal rustfmt error".to_string(), + ))), + }, + _ => Ok(bindings), + } + } + + fn generate(&self, tld: ToplevelDefinition) -> Result { + match tld { + ToplevelDefinition::Type(t) => { + if t.parameterization.is_some() { + return Ok(TokenStream::new()); + } + match t.ty { + ASN1Type::Null => self.generate_null(t), + ASN1Type::Boolean(_) => self.generate_boolean(t), + ASN1Type::Integer(_) => self.generate_integer(t), + ASN1Type::Enumerated(_) => self.generate_enumerated(t), + ASN1Type::BitString(_) => self.generate_bit_string(t), + ASN1Type::CharacterString(_) => self.generate_character_string(t), + ASN1Type::Sequence(_) | ASN1Type::Set(_) => self.generate_sequence_or_set(t), + ASN1Type::SequenceOf(_) | ASN1Type::SetOf(_) => { + self.generate_sequence_or_set_of(t) + } + ASN1Type::ElsewhereDeclaredType(_) => self.generate_typealias(t), + ASN1Type::Choice(_) => self.generate_choice(t), + ASN1Type::OctetString(_) => self.generate_octet_string(t), + ASN1Type::Time(_) => unimplemented!("rasn does not support TIME types yet!"), + ASN1Type::Real(_) => Err(GeneratorError { + kind: GeneratorErrorType::NotYetInplemented, + details: "Real types are currently unsupported!".into(), + top_level_declaration: None, + }), + ASN1Type::ObjectIdentifier(_) => self.generate_oid(t), + ASN1Type::InformationObjectFieldReference(_) + | ASN1Type::EmbeddedPdv + | ASN1Type::External => self.generate_any(t), + ASN1Type::GeneralizedTime(_) => self.generate_generalized_time(t), + ASN1Type::UTCTime(_) => self.generate_utc_time(t), + ASN1Type::ChoiceSelectionType(_) => Err(GeneratorError { + kind: GeneratorErrorType::Asn1TypeMismatch, + details: "Choice selection type should have been resolved at this point!" + .into(), + top_level_declaration: None, + }), + } } + ToplevelDefinition::Value(v) => self.generate_value(v), + ToplevelDefinition::Information(i) => match i.value { + ASN1Information::ObjectSet(_) => self.generate_information_object_set(i), + _ => Ok(TokenStream::new()), + }, } - ToplevelDefinition::Value(v) => generate_value(v), - ToplevelDefinition::Information(i) => match i.value { - ASN1Information::ObjectSet(_) => generate_information_object_set(i), - _ => Ok(TokenStream::new()), - }, } } diff --git a/rasn-compiler/src/generator/rasn/utils.rs b/rasn-compiler/src/generator/rasn/utils.rs index 1a527e9..7223ce3 100644 --- a/rasn-compiler/src/generator/rasn/utils.rs +++ b/rasn-compiler/src/generator/rasn/utils.rs @@ -37,7 +37,7 @@ use self::types::{CharacterString, Constrainable}; use super::*; impl IntegerType { - pub fn to_token_stream(self) -> TokenStream { + fn to_token_stream(self) -> TokenStream { match self { IntegerType::Int8 => quote!(i8), IntegerType::Uint8 => quote!(u8), @@ -57,538 +57,582 @@ pub struct NameType { typ: TokenStream, } -pub fn inner_name(name: &str, parent_name: &str) -> Ident { - format_ident!("{}{}", parent_name, to_rust_title_case(name).to_string()) -} - -pub fn int_type_token(opt_min: Option, opt_max: Option, is_extensible: bool) -> Ident { - if let (Some(min), Some(max)) = (opt_min, opt_max) { - let as_str = if is_extensible { - "Integer" - } else if min >= 0 { - match max { - r if r <= u8::MAX.into() => "u8", - r if r <= u16::MAX.into() => "u16", - r if r <= u32::MAX.into() => "u32", - r if r <= u64::MAX.into() => "u64", - _ => "Integer", - } - } else { - match (min, max) { - (mi, ma) if mi >= i8::MIN.into() && ma <= i8::MAX.into() => "i8", - (mi, ma) if mi >= i16::MIN.into() && ma <= i16::MAX.into() => "i16", - (mi, ma) if mi >= i32::MIN.into() && ma <= i32::MAX.into() => "i32", - (mi, ma) if mi >= i64::MIN.into() && ma <= i64::MAX.into() => "i64", - _ => "Integer", - } - }; - format_ident!("{as_str}") - } else { - format_ident!("Integer") - } -} - -pub fn format_comments(comments: &str) -> Result { - if comments.is_empty() { - Ok(TokenStream::new()) - } else { - let joined = String::from("///") + &comments.replace('\n', "\n ///") + "\n"; - Ok(TokenStream::from_str(&joined)?) - } +#[cfg(test)] +macro_rules! assert_eq_ignore_ws { + ($left:expr, $right:expr) => { + assert_eq!( + $left.replace(|c: char| c.is_whitespace(), ""), + String::from($right).replace(|c: char| c.is_whitespace(), "") + ) + }; } -pub fn format_identifier_annotation(name: &str, comments: &str, ty: &ASN1Type) -> TokenStream { - if comments == " Inner type " - || comments.starts_with(" Anonymous ") - || name.starts_with("ext_group_") - { - let identifier = ty.as_str().replace(' ', "_"); - quote!(identifier = #identifier) - } else { - quote!(identifier = #name) +impl Rasn { + pub(crate) fn inner_name(&self, name: &str, parent_name: &str) -> Ident { + format_ident!( + "{}{}", + parent_name, + self.to_rust_title_case(name).to_string() + ) } -} -pub fn format_range_annotations( - signed: bool, - constraints: &Vec, -) -> Result { - if constraints.is_empty() { - return Ok(TokenStream::new()); - } - let per_constraints = per_visible_range_constraints(signed, constraints)?; - let range_prefix = if per_constraints.is_size_constraint() { - quote!(size) - } else { - quote!(value) - }; - // handle default size constraints - if per_constraints.is_size_constraint() - && !per_constraints.is_extensible() - && per_constraints.min::() == Some(0) - && per_constraints.max::().is_none() - { - return Ok(TokenStream::new()); + pub(crate) fn int_type_token( + &self, + opt_min: Option, + opt_max: Option, + is_extensible: bool, + ) -> Ident { + if let (Some(min), Some(max)) = (opt_min, opt_max) { + let as_str = if is_extensible { + "Integer" + } else if min >= 0 { + match max { + r if r <= u8::MAX.into() => "u8", + r if r <= u16::MAX.into() => "u16", + r if r <= u32::MAX.into() => "u32", + r if r <= u64::MAX.into() => "u64", + _ => "Integer", + } + } else { + match (min, max) { + (mi, ma) if mi >= i8::MIN.into() && ma <= i8::MAX.into() => "i8", + (mi, ma) if mi >= i16::MIN.into() && ma <= i16::MAX.into() => "i16", + (mi, ma) if mi >= i32::MIN.into() && ma <= i32::MAX.into() => "i32", + (mi, ma) if mi >= i64::MIN.into() && ma <= i64::MAX.into() => "i64", + _ => "Integer", + } + }; + format_ident!("{as_str}") + } else { + format_ident!("Integer") + } } - Ok( - match ( - per_constraints.min::(), - per_constraints.max::(), - per_constraints.is_extensible(), - ) { - (Some(min), Some(max), true) if min == max => { - let range = format!("{min}"); - quote!(#range_prefix(#range, extensible)) - } - (Some(min), Some(max), true) => { - let range = format!("{min}..={max}"); - quote!(#range_prefix(#range, extensible)) - } - (Some(min), Some(max), false) if min == max => { - let range = format!("{min}"); - quote!(#range_prefix(#range)) - } - (Some(min), Some(max), false) => { - let range = format!("{min}..={max}"); - quote!(#range_prefix(#range)) - } - (Some(min), None, true) => { - let range = format!("{min}.."); - quote!(#range_prefix(#range, extensible)) - } - (Some(min), None, false) => { - let range = format!("{min}.."); - quote!(#range_prefix(#range)) - } - (None, Some(max), true) => { - let range = format!("..={max}"); - quote!(#range_prefix(#range, extensible)) - } - (None, Some(max), false) => { - let range = format!("..={max}"); - quote!(#range_prefix(#range)) - } - _ => TokenStream::new(), - }, - ) -} -pub fn format_alphabet_annotations( - string_type: CharacterStringType, - constraints: &Vec, -) -> Result { - if constraints.is_empty() { - return Ok(TokenStream::new()); - } - let mut permitted_alphabet = PerVisibleAlphabetConstraints::default_for(string_type); - for c in constraints { - if let Some(mut p) = PerVisibleAlphabetConstraints::try_new(c, string_type)? { - permitted_alphabet += &mut p + pub(crate) fn format_comments(&self, comments: &str) -> Result { + if comments.is_empty() { + Ok(TokenStream::new()) + } else { + let joined = String::from("///") + &comments.replace('\n', "\n ///") + "\n"; + Ok(TokenStream::from_str(&joined)?) } } - permitted_alphabet.finalize(); - let alphabet_unicode = permitted_alphabet - .charset_subsets() - .iter() - .map(|subset| match subset { - CharsetSubset::Single(c) => format!("{}", c.escape_unicode()), - CharsetSubset::Range { from, to } => format!( - "{}..{}", - from.map_or(String::from(""), |c| format!("{}", c.escape_unicode())), - to.map_or(String::from(""), |c| format!("{}", c.escape_unicode())) - ), - }) - .collect::>() - .join(", "); - Ok(if alphabet_unicode.is_empty() { - TokenStream::new() - } else { - quote!(from(#alphabet_unicode)) - }) -} -pub fn format_enum_members(enumerated: &Enumerated) -> TokenStream { - let first_extension_index = enumerated.extensible; - let enumerals = enumerated.members.iter().enumerate().map(|(i, e)| { - let name = to_rust_enum_identifier(&e.name); - let index = Literal::i128_unsuffixed(e.index); - let extension_annotation = if i >= first_extension_index.unwrap_or(usize::MAX) { - quote!(extension_addition) + pub(crate) fn format_identifier_annotation( + &self, + name: &str, + comments: &str, + ty: &ASN1Type, + ) -> TokenStream { + if comments == " Inner type " + || comments.starts_with(" Anonymous ") + || name.starts_with("ext_group_") + { + let identifier = ty.as_str().replace(' ', "_"); + quote!(identifier = #identifier) } else { - TokenStream::new() - }; - let identifier_annotation = if name.to_string() != e.name { - let name = &e.name; quote!(identifier = #name) + } + } + + pub(crate) fn format_range_annotations( + &self, + signed: bool, + constraints: &Vec, + ) -> Result { + if constraints.is_empty() { + return Ok(TokenStream::new()); + } + let per_constraints = per_visible_range_constraints(signed, constraints)?; + let range_prefix = if per_constraints.is_size_constraint() { + quote!(size) } else { - TokenStream::new() + quote!(value) }; - let annotations = join_annotations(vec![extension_annotation, identifier_annotation]); - quote!( - #annotations - #name = #index, + // handle default size constraints + if per_constraints.is_size_constraint() + && !per_constraints.is_extensible() + && per_constraints.min::() == Some(0) + && per_constraints.max::().is_none() + { + return Ok(TokenStream::new()); + } + Ok( + match ( + per_constraints.min::(), + per_constraints.max::(), + per_constraints.is_extensible(), + ) { + (Some(min), Some(max), true) if min == max => { + let range = format!("{min}"); + quote!(#range_prefix(#range, extensible)) + } + (Some(min), Some(max), true) => { + let range = format!("{min}..={max}"); + quote!(#range_prefix(#range, extensible)) + } + (Some(min), Some(max), false) if min == max => { + let range = format!("{min}"); + quote!(#range_prefix(#range)) + } + (Some(min), Some(max), false) => { + let range = format!("{min}..={max}"); + quote!(#range_prefix(#range)) + } + (Some(min), None, true) => { + let range = format!("{min}.."); + quote!(#range_prefix(#range, extensible)) + } + (Some(min), None, false) => { + let range = format!("{min}.."); + quote!(#range_prefix(#range)) + } + (None, Some(max), true) => { + let range = format!("..={max}"); + quote!(#range_prefix(#range, extensible)) + } + (None, Some(max), false) => { + let range = format!("..={max}"); + quote!(#range_prefix(#range)) + } + _ => TokenStream::new(), + }, ) - }); - quote!(#(#enumerals)*) -} + } -pub fn format_tag(tag: Option<&AsnTag>, fallback_to_automatic: bool) -> TokenStream { - if let Some(tag) = tag { - let class = match tag.tag_class { - TagClass::Universal => quote!(universal), - TagClass::Application => quote!(application), - TagClass::Private => quote!(private), - TagClass::ContextSpecific => quote!(context), - }; - let id = Literal::u64_unsuffixed(tag.id); - if tag.environment == TaggingEnvironment::Explicit { - quote!(tag(explicit(#class, #id))) - } else { - quote!(tag(#class, #id)) + pub(crate) fn format_alphabet_annotations( + &self, + string_type: CharacterStringType, + constraints: &Vec, + ) -> Result { + if constraints.is_empty() { + return Ok(TokenStream::new()); } - } else if fallback_to_automatic { - quote!(automatic_tags) - } else { - TokenStream::new() + let mut permitted_alphabet = PerVisibleAlphabetConstraints::default_for(string_type); + for c in constraints { + if let Some(mut p) = PerVisibleAlphabetConstraints::try_new(c, string_type)? { + permitted_alphabet += &mut p + } + } + permitted_alphabet.finalize(); + let alphabet_unicode = permitted_alphabet + .charset_subsets() + .iter() + .map(|subset| match subset { + CharsetSubset::Single(c) => format!("{}", c.escape_unicode()), + CharsetSubset::Range { from, to } => format!( + "{}..{}", + from.map_or(String::from(""), |c| format!("{}", c.escape_unicode())), + to.map_or(String::from(""), |c| format!("{}", c.escape_unicode())) + ), + }) + .collect::>() + .join(", "); + Ok(if alphabet_unicode.is_empty() { + TokenStream::new() + } else { + quote!(from(#alphabet_unicode)) + }) } -} -pub fn format_sequence_or_set_members( - sequence_or_set: &SequenceOrSet, - parent_name: &String, -) -> Result<(TokenStream, Vec), GeneratorError> { - let first_extension_index = sequence_or_set.extensible; - sequence_or_set.members.iter().enumerate().try_fold( - (TokenStream::new(), Vec::new()), - |mut acc, (i, m)| { - let extension_annotation = if i >= first_extension_index.unwrap_or(usize::MAX) - && m.name.starts_with("ext_group_") - { - quote!(extension_addition_group) - } else if i >= first_extension_index.unwrap_or(usize::MAX) { + pub(crate) fn format_enum_members(&self, enumerated: &Enumerated) -> TokenStream { + let first_extension_index = enumerated.extensible; + let enumerals = enumerated.members.iter().enumerate().map(|(i, e)| { + let name = self.to_rust_enum_identifier(&e.name); + let index = Literal::i128_unsuffixed(e.index); + let extension_annotation = if i >= first_extension_index.unwrap_or(usize::MAX) { quote!(extension_addition) } else { TokenStream::new() }; - format_sequence_member(m, parent_name, extension_annotation).map( - |(declaration, name_type)| { - acc.0.append_all([declaration, quote!(, )]); - acc.1.push(name_type); - acc - }, + let identifier_annotation = if name.to_string() != e.name { + let name = &e.name; + quote!(identifier = #name) + } else { + TokenStream::new() + }; + let annotations = + self.join_annotations(vec![extension_annotation, identifier_annotation]); + quote!( + #annotations + #name = #index, ) - }, - ) -} - -fn format_sequence_member( - member: &SequenceOrSetMember, - parent_name: &String, - extension_annotation: TokenStream, -) -> Result<(TokenStream, NameType), GeneratorError> { - let name = to_rust_snake_case(&member.name); - let (mut all_constraints, mut formatted_type_name) = - constraints_and_type_name(&member.ty, &member.name, parent_name)?; - all_constraints.append(&mut member.constraints.clone()); - if (member.is_optional && member.default_value.is_none()) - || member.name.starts_with("ext_group_") - { - formatted_type_name = quote!(Option<#formatted_type_name>); - } - let default_annotation = member - .default_value - .as_ref() - .map(|_| { - let default_fn = default_method_name(parent_name, &member.name); - quote!(default = #default_fn) - }) - .unwrap_or_default(); - let range_annotations = - format_range_annotations(matches!(member.ty, ASN1Type::Integer(_)), &all_constraints)?; - let alphabet_annotations = if let ASN1Type::CharacterString(c_string) = &member.ty { - format_alphabet_annotations(c_string.ty, &all_constraints)? - } else { - TokenStream::new() - }; - let mut annotation_items = vec![ - extension_annotation, - range_annotations, - alphabet_annotations, - format_tag(member.tag.as_ref(), false), - default_annotation, - ]; - if name != member.name || member.name.starts_with("ext_group_") { - annotation_items.push(format_identifier_annotation(&member.name, "", &member.ty)); + }); + quote!(#(#enumerals)*) } - let annotations = join_annotations(annotation_items); - Ok(( - quote! { - #annotations - pub #name: #formatted_type_name - }, - NameType { - name, - typ: formatted_type_name, - }, - )) -} -pub fn format_choice_options( - choice: &Choice, - parent_name: &String, -) -> Result { - let first_extension_index = choice.extensible; - let options = choice - .options - .iter() - .enumerate() - .map(|(i, o)| { - let extension_annotation = if i >= first_extension_index.unwrap_or(usize::MAX) - && o.name.starts_with("ext_group_") - { - quote!(extension_addition_group) - } else if i >= first_extension_index.unwrap_or(usize::MAX) { - quote!(extension_addition) - } else { - TokenStream::new() + pub(crate) fn format_tag( + &self, + tag: Option<&AsnTag>, + fallback_to_automatic: bool, + ) -> TokenStream { + if let Some(tag) = tag { + let class = match tag.tag_class { + TagClass::Universal => quote!(universal), + TagClass::Application => quote!(application), + TagClass::Private => quote!(private), + TagClass::ContextSpecific => quote!(context), }; - let name = to_rust_enum_identifier(&o.name); - format_choice_option(name, o, parent_name, extension_annotation) - }) - .collect::, _>>()?; - Ok(quote!(#(#options)*)) -} + let id = Literal::u64_unsuffixed(tag.id); + if tag.environment == TaggingEnvironment::Explicit { + quote!(tag(explicit(#class, #id))) + } else { + quote!(tag(#class, #id)) + } + } else if fallback_to_automatic { + quote!(automatic_tags) + } else { + TokenStream::new() + } + } -fn format_choice_option( - name: Ident, - member: &ChoiceOption, - parent_name: &String, - extension_annotation: TokenStream, -) -> Result { - let (mut all_constraints, formatted_type_name) = - constraints_and_type_name(&member.ty, &member.name, parent_name)?; - all_constraints.append(&mut member.constraints.clone()); - let range_annotations = - format_range_annotations(matches!(member.ty, ASN1Type::Integer(_)), &all_constraints)?; - let alphabet_annotations = if let ASN1Type::CharacterString(c_string) = &member.ty { - format_alphabet_annotations(c_string.ty, &all_constraints)? - } else { - TokenStream::new() - }; - let mut annotation_items = vec![ - extension_annotation, - range_annotations, - alphabet_annotations, - format_tag(member.tag.as_ref(), false), - ]; - if name != member.name || member.name.starts_with("ext_group_") { - annotation_items.push(format_identifier_annotation(&member.name, "", &member.ty)); + pub(crate) fn format_sequence_or_set_members( + &self, + sequence_or_set: &SequenceOrSet, + parent_name: &String, + ) -> Result<(TokenStream, Vec), GeneratorError> { + let first_extension_index = sequence_or_set.extensible; + sequence_or_set.members.iter().enumerate().try_fold( + (TokenStream::new(), Vec::new()), + |mut acc, (i, m)| { + let extension_annotation = if i >= first_extension_index.unwrap_or(usize::MAX) + && m.name.starts_with("ext_group_") + { + quote!(extension_addition_group) + } else if i >= first_extension_index.unwrap_or(usize::MAX) { + quote!(extension_addition) + } else { + TokenStream::new() + }; + self.format_sequence_member(m, parent_name, extension_annotation) + .map(|(declaration, name_type)| { + acc.0.append_all([declaration, quote!(, )]); + acc.1.push(name_type); + acc + }) + }, + ) } - let annotations = join_annotations(annotation_items); - Ok(quote! { - #annotations - #name(#formatted_type_name), - }) -} -fn constraints_and_type_name( - ty: &ASN1Type, - name: &String, - parent_name: &String, -) -> Result<(Vec, TokenStream), GeneratorError> { - Ok(match ty { - ASN1Type::Null => (vec![], quote!(())), - ASN1Type::Boolean(b) => (b.constraints.clone(), quote!(bool)), - ASN1Type::Integer(i) => { - let per_constraints = per_visible_range_constraints(true, &i.constraints)?; - ( - i.constraints.clone(), - int_type_token( - per_constraints.min(), - per_constraints.max(), - per_constraints.is_extensible(), - ) - .to_token_stream(), - ) + pub(crate) fn format_sequence_member( + &self, + member: &SequenceOrSetMember, + parent_name: &String, + extension_annotation: TokenStream, + ) -> Result<(TokenStream, NameType), GeneratorError> { + let name = self.to_rust_snake_case(&member.name); + let (mut all_constraints, mut formatted_type_name) = + self.constraints_and_type_name(&member.ty, &member.name, parent_name)?; + all_constraints.append(&mut member.constraints.clone()); + if (member.is_optional && member.default_value.is_none()) + || member.name.starts_with("ext_group_") + { + formatted_type_name = quote!(Option<#formatted_type_name>); } - ASN1Type::Real(_) => (vec![], quote!(f64)), - ASN1Type::ObjectIdentifier(o) => (o.constraints.clone(), quote!(ObjectIdentifier)), - ASN1Type::BitString(b) => (b.constraints.clone(), quote!(BitString)), - ASN1Type::OctetString(o) => (o.constraints.clone(), quote!(OctetString)), - ASN1Type::GeneralizedTime(o) => (o.constraints.clone(), quote!(GeneralizedTime)), - ASN1Type::UTCTime(o) => (o.constraints.clone(), quote!(UtcTime)), - ASN1Type::Time(_) => { - return Err(GeneratorError { - details: "rasn does not support TIME types yet!".into(), - top_level_declaration: None, - kind: GeneratorErrorType::NotYetInplemented, + let default_annotation = member + .default_value + .as_ref() + .map(|_| { + let default_fn = self.default_method_name(parent_name, &member.name); + quote!(default = #default_fn) }) + .unwrap_or_default(); + let range_annotations = self.format_range_annotations( + matches!(member.ty, ASN1Type::Integer(_)), + &all_constraints, + )?; + let alphabet_annotations = if let ASN1Type::CharacterString(c_string) = &member.ty { + self.format_alphabet_annotations(c_string.ty, &all_constraints)? + } else { + TokenStream::new() + }; + let mut annotation_items = vec![ + extension_annotation, + range_annotations, + alphabet_annotations, + self.format_tag(member.tag.as_ref(), false), + default_annotation, + ]; + if name != member.name || member.name.starts_with("ext_group_") { + annotation_items.push(self.format_identifier_annotation(&member.name, "", &member.ty)); } - ASN1Type::CharacterString(c) => (c.constraints.clone(), string_type(&c.ty)?), - ASN1Type::Enumerated(_) - | ASN1Type::Choice(_) - | ASN1Type::Sequence(_) - | ASN1Type::SetOf(_) - | ASN1Type::Set(_) => (vec![], inner_name(name, parent_name).to_token_stream()), - ASN1Type::SequenceOf(s) => { - let (_, inner_type) = constraints_and_type_name(&s.element_type, name, parent_name)?; - (s.constraints().clone(), quote!(SequenceOf<#inner_type>)) - } - ASN1Type::ElsewhereDeclaredType(e) => ( - e.constraints.clone(), - to_rust_title_case(&e.identifier).to_token_stream(), - ), - ASN1Type::InformationObjectFieldReference(_) - | ASN1Type::EmbeddedPdv - | ASN1Type::External => (vec![], quote!(Any)), - ASN1Type::ChoiceSelectionType(_) => unreachable!(), - }) -} + let annotations = self.join_annotations(annotation_items); + Ok(( + quote! { + #annotations + pub #name: #formatted_type_name + }, + NameType { + name, + typ: formatted_type_name, + }, + )) + } -pub fn string_type(c_type: &CharacterStringType) -> Result { - match c_type { - CharacterStringType::NumericString => Ok(quote!(NumericString)), - CharacterStringType::VisibleString => Ok(quote!(VisibleString)), - CharacterStringType::IA5String => Ok(quote!(Ia5String)), - CharacterStringType::TeletexString => Ok(quote!(TeletexString)), - CharacterStringType::VideotexString => Err(GeneratorError { - kind: GeneratorErrorType::NotYetInplemented, - details: "VideotexString is currently unsupported!".into(), - top_level_declaration: None, - }), - CharacterStringType::GraphicString => Err(GeneratorError { - kind: GeneratorErrorType::NotYetInplemented, - details: "GraphicString is currently unsupported!".into(), - top_level_declaration: None, - }), - CharacterStringType::GeneralString => Ok(quote!(GeneralString)), - CharacterStringType::UniversalString => Err(GeneratorError { - kind: GeneratorErrorType::NotYetInplemented, - details: "UniversalString is currently unsupported!".into(), - top_level_declaration: None, - }), - CharacterStringType::UTF8String => Ok(quote!(Utf8String)), - CharacterStringType::BMPString => Ok(quote!(BmpString)), - CharacterStringType::PrintableString => Ok(quote!(PrintableString)), + pub(crate) fn format_choice_options( + &self, + choice: &Choice, + parent_name: &String, + ) -> Result { + let first_extension_index = choice.extensible; + let options = choice + .options + .iter() + .enumerate() + .map(|(i, o)| { + let extension_annotation = if i >= first_extension_index.unwrap_or(usize::MAX) + && o.name.starts_with("ext_group_") + { + quote!(extension_addition_group) + } else if i >= first_extension_index.unwrap_or(usize::MAX) { + quote!(extension_addition) + } else { + TokenStream::new() + }; + let name = self.to_rust_enum_identifier(&o.name); + self.format_choice_option(name, o, parent_name, extension_annotation) + }) + .collect::, _>>()?; + Ok(quote!(#(#options)*)) } -} -pub fn join_annotations(elements: Vec) -> TokenStream { - let mut not_empty_exprs = elements.into_iter().filter(|ts| !ts.is_empty()); - if let Some(mut annotations) = not_empty_exprs.next() { - for elem in not_empty_exprs { - annotations.append(Punct::new(',', Spacing::Alone)); - annotations.append_all(elem); + pub(crate) fn format_choice_option( + &self, + name: Ident, + member: &ChoiceOption, + parent_name: &String, + extension_annotation: TokenStream, + ) -> Result { + let (mut all_constraints, formatted_type_name) = + self.constraints_and_type_name(&member.ty, &member.name, parent_name)?; + all_constraints.append(&mut member.constraints.clone()); + let range_annotations = self.format_range_annotations( + matches!(member.ty, ASN1Type::Integer(_)), + &all_constraints, + )?; + let alphabet_annotations = if let ASN1Type::CharacterString(c_string) = &member.ty { + self.format_alphabet_annotations(c_string.ty, &all_constraints)? + } else { + TokenStream::new() + }; + let mut annotation_items = vec![ + extension_annotation, + range_annotations, + alphabet_annotations, + self.format_tag(member.tag.as_ref(), false), + ]; + if name != member.name || member.name.starts_with("ext_group_") { + annotation_items.push(self.format_identifier_annotation(&member.name, "", &member.ty)); } - quote!(#[rasn(#annotations)]) - } else { - quote!() + let annotations = self.join_annotations(annotation_items); + Ok(quote! { + #annotations + #name(#formatted_type_name), + }) } -} -pub fn default_method_name(parent_name: &str, field_name: &str) -> String { - format!( - "{}_{}_default", - to_rust_snake_case(parent_name), - to_rust_snake_case(field_name) - ) -} + pub(crate) fn constraints_and_type_name( + &self, + ty: &ASN1Type, + name: &String, + parent_name: &String, + ) -> Result<(Vec, TokenStream), GeneratorError> { + Ok(match ty { + ASN1Type::Null => (vec![], quote!(())), + ASN1Type::Boolean(b) => (b.constraints.clone(), quote!(bool)), + ASN1Type::Integer(i) => { + let per_constraints = per_visible_range_constraints(true, &i.constraints)?; + ( + i.constraints.clone(), + self.int_type_token( + per_constraints.min(), + per_constraints.max(), + per_constraints.is_extensible(), + ) + .to_token_stream(), + ) + } + ASN1Type::Real(_) => (vec![], quote!(f64)), + ASN1Type::ObjectIdentifier(o) => (o.constraints.clone(), quote!(ObjectIdentifier)), + ASN1Type::BitString(b) => (b.constraints.clone(), quote!(BitString)), + ASN1Type::OctetString(o) => (o.constraints.clone(), quote!(OctetString)), + ASN1Type::GeneralizedTime(o) => (o.constraints.clone(), quote!(GeneralizedTime)), + ASN1Type::UTCTime(o) => (o.constraints.clone(), quote!(UtcTime)), + ASN1Type::Time(_) => { + return Err(GeneratorError { + details: "rasn does not support TIME types yet!".into(), + top_level_declaration: None, + kind: GeneratorErrorType::NotYetInplemented, + }) + } + ASN1Type::CharacterString(c) => (c.constraints.clone(), self.string_type(&c.ty)?), + ASN1Type::Enumerated(_) + | ASN1Type::Choice(_) + | ASN1Type::Sequence(_) + | ASN1Type::SetOf(_) + | ASN1Type::Set(_) => (vec![], self.inner_name(name, parent_name).to_token_stream()), + ASN1Type::SequenceOf(s) => { + let (_, inner_type) = + self.constraints_and_type_name(&s.element_type, name, parent_name)?; + (s.constraints().clone(), quote!(SequenceOf<#inner_type>)) + } + ASN1Type::ElsewhereDeclaredType(e) => ( + e.constraints.clone(), + self.to_rust_title_case(&e.identifier).to_token_stream(), + ), + ASN1Type::InformationObjectFieldReference(_) + | ASN1Type::EmbeddedPdv + | ASN1Type::External => (vec![], quote!(Any)), + ASN1Type::ChoiceSelectionType(_) => unreachable!(), + }) + } -pub fn format_default_methods( - members: &Vec, - parent_name: &str, -) -> Result { - let mut output = TokenStream::new(); - for member in members { - if let Some(value) = member.default_value.as_ref() { - let val = value_to_tokens( - value, - Some(&to_rust_title_case( - &type_to_tokens(&member.ty)?.to_string(), - )), - )?; - let ty = type_to_tokens(&member.ty)?; - let method_name = - TokenStream::from_str(&default_method_name(parent_name, &member.name))?; - output.append_all(quote! { - fn #method_name() -> #ty { - #val - } - }); + pub(crate) fn string_type( + &self, + c_type: &CharacterStringType, + ) -> Result { + match c_type { + CharacterStringType::NumericString => Ok(quote!(NumericString)), + CharacterStringType::VisibleString => Ok(quote!(VisibleString)), + CharacterStringType::IA5String => Ok(quote!(Ia5String)), + CharacterStringType::TeletexString => Ok(quote!(TeletexString)), + CharacterStringType::VideotexString => Err(GeneratorError { + kind: GeneratorErrorType::NotYetInplemented, + details: "VideotexString is currently unsupported!".into(), + top_level_declaration: None, + }), + CharacterStringType::GraphicString => Err(GeneratorError { + kind: GeneratorErrorType::NotYetInplemented, + details: "GraphicString is currently unsupported!".into(), + top_level_declaration: None, + }), + CharacterStringType::GeneralString => Ok(quote!(GeneralString)), + CharacterStringType::UniversalString => Err(GeneratorError { + kind: GeneratorErrorType::NotYetInplemented, + details: "UniversalString is currently unsupported!".into(), + top_level_declaration: None, + }), + CharacterStringType::UTF8String => Ok(quote!(Utf8String)), + CharacterStringType::BMPString => Ok(quote!(BmpString)), + CharacterStringType::PrintableString => Ok(quote!(PrintableString)), } } - Ok(output) -} -pub fn type_to_tokens(ty: &ASN1Type) -> Result { - match ty { - ASN1Type::Null => Ok(quote!(())), - ASN1Type::Boolean(_) => Ok(quote!(bool)), - ASN1Type::Integer(i) => Ok(i.int_type().to_token_stream()), - ASN1Type::Real(_) => Ok(quote!(f64)), - ASN1Type::BitString(_) => Ok(quote!(BitString)), - ASN1Type::OctetString(_) => Ok(quote!(OctetString)), - ASN1Type::CharacterString(CharacterString { ty, .. }) => string_type(ty), - ASN1Type::Enumerated(_) => Err(error!( - NotYetInplemented, - "Enumerated values are currently unsupported!" - )), - ASN1Type::Choice(_) => Err(error!( - NotYetInplemented, - "Choice values are currently unsupported!" - )), - ASN1Type::Sequence(_) => Err(error!( - NotYetInplemented, - "Sequence values are currently unsupported!" - )), - ASN1Type::SetOf(so) | ASN1Type::SequenceOf(so) => { - let inner = type_to_tokens(&so.element_type)?; - Ok(quote!(SequenceOf<#inner>)) + pub(crate) fn join_annotations(&self, elements: Vec) -> TokenStream { + let mut not_empty_exprs = elements.into_iter().filter(|ts| !ts.is_empty()); + if let Some(mut annotations) = not_empty_exprs.next() { + for elem in not_empty_exprs { + annotations.append(Punct::new(',', Spacing::Alone)); + annotations.append_all(elem); + } + quote!(#[rasn(#annotations)]) + } else { + quote!() + } + } + + pub(crate) fn default_method_name(&self, parent_name: &str, field_name: &str) -> String { + format!( + "{}_{}_default", + self.to_rust_snake_case(parent_name), + self.to_rust_snake_case(field_name) + ) + } + + pub(crate) fn format_default_methods( + &self, + members: &Vec, + parent_name: &str, + ) -> Result { + let mut output = TokenStream::new(); + for member in members { + if let Some(value) = member.default_value.as_ref() { + let val = self.value_to_tokens( + value, + Some(&self.to_rust_title_case(&self.type_to_tokens(&member.ty)?.to_string())), + )?; + let ty = self.type_to_tokens(&member.ty)?; + let method_name = + TokenStream::from_str(&self.default_method_name(parent_name, &member.name))?; + output.append_all(quote! { + fn #method_name() -> #ty { + #val + } + }); + } } - ASN1Type::ObjectIdentifier(_) => Err(error!( - NotYetInplemented, - "Object Identifier values are currently unsupported!" - )), - ASN1Type::Set(_) => Err(error!( - NotYetInplemented, - "Set values are currently unsupported!" - )), - ASN1Type::ElsewhereDeclaredType(e) => Ok(to_rust_title_case(&e.identifier)), - ASN1Type::InformationObjectFieldReference(_) => Err(error!( - NotYetInplemented, - "Information Object field reference values are currently unsupported!" - )), - ASN1Type::Time(_) => Err(error!( - NotYetInplemented, - "Time values are currently unsupported!" - )), - ASN1Type::GeneralizedTime(_) => Ok(quote!(GeneralizedTime)), - ASN1Type::UTCTime(_) => Ok(quote!(UtcTime)), - ASN1Type::EmbeddedPdv | ASN1Type::External => Ok(quote!(Any)), - ASN1Type::ChoiceSelectionType(c) => { - let choice = to_rust_title_case(&c.choice_name); - let option = to_rust_enum_identifier(&c.selected_option); - Ok(quote!(#choice::#option)) + Ok(output) + } + + pub(crate) fn type_to_tokens(&self, ty: &ASN1Type) -> Result { + match ty { + ASN1Type::Null => Ok(quote!(())), + ASN1Type::Boolean(_) => Ok(quote!(bool)), + ASN1Type::Integer(i) => Ok(i.int_type().to_token_stream()), + ASN1Type::Real(_) => Ok(quote!(f64)), + ASN1Type::BitString(_) => Ok(quote!(BitString)), + ASN1Type::OctetString(_) => Ok(quote!(OctetString)), + ASN1Type::CharacterString(CharacterString { ty, .. }) => self.string_type(ty), + ASN1Type::Enumerated(_) => Err(error!( + NotYetInplemented, + "Enumerated values are currently unsupported!" + )), + ASN1Type::Choice(_) => Err(error!( + NotYetInplemented, + "Choice values are currently unsupported!" + )), + ASN1Type::Sequence(_) => Err(error!( + NotYetInplemented, + "Sequence values are currently unsupported!" + )), + ASN1Type::SetOf(so) | ASN1Type::SequenceOf(so) => { + let inner = self.type_to_tokens(&so.element_type)?; + Ok(quote!(SequenceOf<#inner>)) + } + ASN1Type::ObjectIdentifier(_) => Err(error!( + NotYetInplemented, + "Object Identifier values are currently unsupported!" + )), + ASN1Type::Set(_) => Err(error!( + NotYetInplemented, + "Set values are currently unsupported!" + )), + ASN1Type::ElsewhereDeclaredType(e) => Ok(self.to_rust_title_case(&e.identifier)), + ASN1Type::InformationObjectFieldReference(_) => Err(error!( + NotYetInplemented, + "Information Object field reference values are currently unsupported!" + )), + ASN1Type::Time(_) => Err(error!( + NotYetInplemented, + "Time values are currently unsupported!" + )), + ASN1Type::GeneralizedTime(_) => Ok(quote!(GeneralizedTime)), + ASN1Type::UTCTime(_) => Ok(quote!(UtcTime)), + ASN1Type::EmbeddedPdv | ASN1Type::External => Ok(quote!(Any)), + ASN1Type::ChoiceSelectionType(c) => { + let choice = self.to_rust_title_case(&c.choice_name); + let option = self.to_rust_enum_identifier(&c.selected_option); + Ok(quote!(#choice::#option)) + } } } -} -pub fn value_to_tokens( - value: &ASN1Value, - type_name: Option<&TokenStream>, -) -> Result { - match value { - ASN1Value::All => Err(error!( - NotYetInplemented, - "rasn does not support ALL values." - )), - ASN1Value::Null => Ok(quote!(())), - ASN1Value::Choice { - type_name: tn, - variant_name: i, - inner_value: v, - } => { - let rust_ty_name = tn + pub(crate) fn value_to_tokens( + &self, + value: &ASN1Value, + type_name: Option<&TokenStream>, + ) -> Result { + match value { + ASN1Value::All => Err(error!( + NotYetInplemented, + "rasn does not support ALL values." + )), + ASN1Value::Null => Ok(quote!(())), + ASN1Value::Choice { + type_name: tn, + variant_name: i, + inner_value: v, + } => { + let rust_ty_name = tn .as_ref() .map(|t| if t.starts_with(INTERNAL_NESTED_TYPE_NAME_PREFIX) { let split: Vec<&str> = t.split('$').collect(); @@ -598,309 +642,394 @@ pub fn value_to_tokens( ..Default::default() }) } else { - Ok(inner_name(split[1], split[2]).to_token_stream()) + Ok(self.inner_name(split[1], split[2]).to_token_stream()) } } else { - Ok(to_rust_title_case(t)) + Ok(self.to_rust_title_case(t)) }).transpose()?; - if let Some(ty_n) = rust_ty_name.as_ref().or(type_name) { - let option = to_rust_enum_identifier(i); - let inner = value_to_tokens(v, None)?; - Ok(quote!(#ty_n::#option(#inner))) - } else { - Err(error!( - Unidentified, - "A type name is needed to stringify choice value {:?}", value - )) + if let Some(ty_n) = rust_ty_name.as_ref().or(type_name) { + let option = self.to_rust_enum_identifier(i); + let inner = self.value_to_tokens(v, None)?; + Ok(quote!(#ty_n::#option(#inner))) + } else { + Err(error!( + Unidentified, + "A type name is needed to stringify choice value {:?}", value + )) + } } - } - ASN1Value::OctetString(o) => { - let bytes = o.iter().map(|byte| Literal::u8_unsuffixed(*byte)); - Ok(quote!(>::from(&[#(#bytes),*]))) - } - ASN1Value::SequenceOrSet(_) => Err(error!( - Unidentified, - "Unexpectedly encountered unlinked struct-like ASN1 value!" - )), - ASN1Value::LinkedStructLikeValue(fields) => { - if let Some(ty_n) = type_name { - let tokenized_fields = fields - .iter() - .map(|(_, ty, val)| { - value_to_tokens(val.value(), type_to_tokens(ty).ok().as_ref()) - }) - .collect::, _>>()?; - Ok(quote!(#ty_n ::new(#(#tokenized_fields),*))) - } else { - Err(error!( - Unidentified, - "A type name is needed to stringify sequence value {:?}", value - )) + ASN1Value::OctetString(o) => { + let bytes = o.iter().map(|byte| Literal::u8_unsuffixed(*byte)); + Ok(quote!(>::from(&[#(#bytes),*]))) } - } - ASN1Value::Boolean(b) => Ok(b.to_token_stream()), - ASN1Value::Integer(i) => Ok(Literal::i128_unsuffixed(*i).to_token_stream()), - ASN1Value::String(s) => Ok(s.to_token_stream()), - ASN1Value::Real(r) => Ok(r.to_token_stream()), - ASN1Value::BitString(b) => { - let bits = b.iter().map(|bit| bit.to_token_stream()); - Ok(quote!([#(#bits),*].into_iter().collect())) - } - ASN1Value::EnumeratedValue { - enumerated, - enumerable, - } => { - let enum_name = to_rust_title_case(enumerated); - let enumerable_id = to_rust_enum_identifier(enumerable); - Ok(quote!(#enum_name::#enumerable_id)) - } - ASN1Value::LinkedElsewhereDefinedValue { identifier: e, .. } - | ASN1Value::ElsewhereDeclaredValue { identifier: e, .. } => { - Ok(to_rust_const_case(e).to_token_stream()) - } - ASN1Value::ObjectIdentifier(oid) => { - let arcs = oid - .0 - .iter() - .filter_map(|arc| arc.number.map(|id| id.to_token_stream())); - Ok(quote!(Oid::const_new(&[#(#arcs),*]).to_owned())) - } - ASN1Value::Time(t) => match type_name { - Some(time_type) => Ok(quote!(#t.parse::<#time_type>().unwrap())), - None => Ok(quote!(#t.parse::<_>().unwrap())), - }, - ASN1Value::LinkedArrayLikeValue(seq) => { - let elems = seq - .iter() - .map(|v| value_to_tokens(v, None)) - .collect::, _>>()?; - Ok(quote!(alloc::vec![#(#elems),*])) - } - ASN1Value::LinkedNestedValue { supertypes, value } => { - fn nester(s: TokenStream, mut types: Vec) -> TokenStream { - match types.pop() { - Some(t) => { - let ident = to_rust_title_case(&t); - nester(quote!(#ident(#s)), types) - } - None => s, + ASN1Value::SequenceOrSet(_) => Err(error!( + Unidentified, + "Unexpectedly encountered unlinked struct-like ASN1 value!" + )), + ASN1Value::LinkedStructLikeValue(fields) => { + if let Some(ty_n) = type_name { + let tokenized_fields = fields + .iter() + .map(|(_, ty, val)| { + self.value_to_tokens(val.value(), self.type_to_tokens(ty).ok().as_ref()) + }) + .collect::, _>>()?; + Ok(quote!(#ty_n ::new(#(#tokenized_fields),*))) + } else { + Err(error!( + Unidentified, + "A type name is needed to stringify sequence value {:?}", value + )) } } - Ok(nester( - value_to_tokens(value, type_name)?, - supertypes.clone(), - )) - } - ASN1Value::LinkedIntValue { - integer_type, - value, - } => { - let val = Literal::i128_unsuffixed(*value); - match integer_type { - IntegerType::Unbounded => Ok(quote!(Integer::from(#val))), - _ => Ok(val.to_token_stream()), + ASN1Value::Boolean(b) => Ok(b.to_token_stream()), + ASN1Value::Integer(i) => Ok(Literal::i128_unsuffixed(*i).to_token_stream()), + ASN1Value::String(s) => Ok(s.to_token_stream()), + ASN1Value::Real(r) => Ok(r.to_token_stream()), + ASN1Value::BitString(b) => { + let bits = b.iter().map(|bit| bit.to_token_stream()); + Ok(quote!([#(#bits),*].into_iter().collect())) } - } - ASN1Value::LinkedCharStringValue(string_type, value) => { - let val = value.to_token_stream(); - match string_type { - CharacterStringType::NumericString => { - Ok(quote!(NumericString::try_from(#val).unwrap())) - } - CharacterStringType::VisibleString => { - Ok(quote!(VisibleString::try_from(#val).unwrap())) + ASN1Value::EnumeratedValue { + enumerated, + enumerable, + } => { + let enum_name = self.to_rust_title_case(enumerated); + let enumerable_id = self.to_rust_enum_identifier(enumerable); + Ok(quote!(#enum_name::#enumerable_id)) + } + ASN1Value::LinkedElsewhereDefinedValue { identifier: e, .. } + | ASN1Value::ElsewhereDeclaredValue { identifier: e, .. } => { + Ok(self.to_rust_const_case(e).to_token_stream()) + } + ASN1Value::ObjectIdentifier(oid) => { + let arcs = oid + .0 + .iter() + .filter_map(|arc| arc.number.map(|id| id.to_token_stream())); + Ok(quote!(Oid::const_new(&[#(#arcs),*]).to_owned())) + } + ASN1Value::Time(t) => match type_name { + Some(time_type) => Ok(quote!(#t.parse::<#time_type>().unwrap())), + None => Ok(quote!(#t.parse::<_>().unwrap())), + }, + ASN1Value::LinkedArrayLikeValue(seq) => { + let elems = seq + .iter() + .map(|v| self.value_to_tokens(v, None)) + .collect::, _>>()?; + Ok(quote!(alloc::vec![#(#elems),*])) + } + ASN1Value::LinkedNestedValue { supertypes, value } => { + fn nester(generator: &Rasn, s: TokenStream, mut types: Vec) -> TokenStream { + match types.pop() { + Some(t) => { + let ident = generator.to_rust_title_case(&t); + nester(generator, quote!(#ident(#s)), types) + } + None => s, + } } - CharacterStringType::IA5String => Ok(quote!(Ia5String::try_from(#val).unwrap())), - CharacterStringType::UTF8String => Ok(quote!(String::from(#val))), - CharacterStringType::BMPString => Ok(quote!(BmpString::try_from(#val).unwrap())), - CharacterStringType::PrintableString => { - Ok(quote!(PrintableString::try_from(#val).unwrap())) + Ok(nester( + &self, + self.value_to_tokens(value, type_name)?, + supertypes.clone(), + )) + } + ASN1Value::LinkedIntValue { + integer_type, + value, + } => { + let val = Literal::i128_unsuffixed(*value); + match integer_type { + IntegerType::Unbounded => Ok(quote!(Integer::from(#val))), + _ => Ok(val.to_token_stream()), } - CharacterStringType::GeneralString => { - Ok(quote!(GeneralString::try_from(String::from(#val)).unwrap())) + } + ASN1Value::LinkedCharStringValue(string_type, value) => { + let val = value.to_token_stream(); + match string_type { + CharacterStringType::NumericString => { + Ok(quote!(NumericString::try_from(#val).unwrap())) + } + CharacterStringType::VisibleString => { + Ok(quote!(VisibleString::try_from(#val).unwrap())) + } + CharacterStringType::IA5String => { + Ok(quote!(Ia5String::try_from(#val).unwrap())) + } + CharacterStringType::UTF8String => Ok(quote!(String::from(#val))), + CharacterStringType::BMPString => { + Ok(quote!(BmpString::try_from(#val).unwrap())) + } + CharacterStringType::PrintableString => { + Ok(quote!(PrintableString::try_from(#val).unwrap())) + } + CharacterStringType::GeneralString => { + Ok(quote!(GeneralString::try_from(String::from(#val)).unwrap())) + } + CharacterStringType::VideotexString + | CharacterStringType::GraphicString + | CharacterStringType::UniversalString + | CharacterStringType::TeletexString => Err(GeneratorError::new( + None, + &format!("{:?} values are currently unsupported!", string_type), + GeneratorErrorType::NotYetInplemented, + )), } - CharacterStringType::VideotexString - | CharacterStringType::GraphicString - | CharacterStringType::UniversalString - | CharacterStringType::TeletexString => Err(GeneratorError::new( - None, - &format!("{:?} values are currently unsupported!", string_type), - GeneratorErrorType::NotYetInplemented, - )), } } } -} -pub fn format_nested_sequence_members( - sequence_or_set: &SequenceOrSet, - parent_name: &String, -) -> Result, GeneratorError> { - sequence_or_set - .members - .iter() - .filter(|m| needs_unnesting(&m.ty)) - .map(|m| { - generate(ToplevelDefinition::Type(ToplevelTypeDefinition { - parameterization: None, - comments: " Inner type ".into(), - name: inner_name(&m.name, parent_name).to_string(), - ty: m.ty.clone(), - tag: None, - index: None, - })) - }) - .collect::, _>>() -} + pub(crate) fn format_nested_sequence_members( + &self, + sequence_or_set: &SequenceOrSet, + parent_name: &String, + ) -> Result, GeneratorError> { + sequence_or_set + .members + .iter() + .filter(|m| self.needs_unnesting(&m.ty)) + .map(|m| { + self.generate(ToplevelDefinition::Type(ToplevelTypeDefinition { + parameterization: None, + comments: " Inner type ".into(), + name: self.inner_name(&m.name, parent_name).to_string(), + ty: m.ty.clone(), + tag: None, + index: None, + })) + }) + .collect::, _>>() + } -fn needs_unnesting(ty: &ASN1Type) -> bool { - match ty { - ASN1Type::Enumerated(_) - | ASN1Type::Choice(_) - | ASN1Type::Sequence(_) - | ASN1Type::Set(_) => true, - ASN1Type::SequenceOf(SequenceOrSetOf { element_type, .. }) - | ASN1Type::SetOf(SequenceOrSetOf { element_type, .. }) => needs_unnesting(element_type), - _ => false, + pub(crate) fn needs_unnesting(&self, ty: &ASN1Type) -> bool { + match ty { + ASN1Type::Enumerated(_) + | ASN1Type::Choice(_) + | ASN1Type::Sequence(_) + | ASN1Type::Set(_) => true, + ASN1Type::SequenceOf(SequenceOrSetOf { element_type, .. }) + | ASN1Type::SetOf(SequenceOrSetOf { element_type, .. }) => { + self.needs_unnesting(element_type) + } + _ => false, + } } -} -pub fn format_nested_choice_options( - choice: &Choice, - parent_name: &String, -) -> Result, GeneratorError> { - choice - .options - .iter() - .filter(|m| { - matches!( - m.ty, - ASN1Type::Enumerated(_) - | ASN1Type::Choice(_) - | ASN1Type::Sequence(_) - | ASN1Type::SequenceOf(_) - | ASN1Type::Set(_) - ) - }) - .map(|m| { - generate(ToplevelDefinition::Type(ToplevelTypeDefinition { - parameterization: None, - comments: " Inner type ".into(), - name: inner_name(&m.name, parent_name).to_string(), - ty: m.ty.clone(), - tag: None, - index: None, - })) - }) - .collect::, _>>() -} + pub(crate) fn format_nested_choice_options( + &self, + choice: &Choice, + parent_name: &String, + ) -> Result, GeneratorError> { + choice + .options + .iter() + .filter(|m| { + matches!( + m.ty, + ASN1Type::Enumerated(_) + | ASN1Type::Choice(_) + | ASN1Type::Sequence(_) + | ASN1Type::SequenceOf(_) + | ASN1Type::Set(_) + ) + }) + .map(|m| { + self.generate(ToplevelDefinition::Type(ToplevelTypeDefinition { + parameterization: None, + comments: " Inner type ".into(), + name: self.inner_name(&m.name, parent_name).to_string(), + ty: m.ty.clone(), + tag: None, + index: None, + })) + }) + .collect::, _>>() + } -pub fn format_new_impl(name: &TokenStream, name_types: Vec) -> TokenStream { - let args = name_types.iter().map(|nt| { - let name = &nt.name; - let ty = &nt.typ; - quote!(#name: #ty) - }); - let instance = name_types.iter().map(|nt| &nt.name); - quote! { - impl #name { - pub fn new(#(#args),*) -> Self { - Self { #(#instance),* } + pub(crate) fn format_new_impl( + &self, + name: &TokenStream, + name_types: Vec, + ) -> TokenStream { + let args = name_types.iter().map(|nt| { + let name = &nt.name; + let ty = &nt.typ; + quote!(#name: #ty) + }); + let instance = name_types.iter().map(|nt| &nt.name); + quote! { + impl #name { + pub fn new(#(#args),*) -> Self { + Self { #(#instance),* } + } } } } -} -pub fn format_sequence_or_set_of_item_type( - type_name: String, - first_item: Option<&ASN1Value>, -) -> TokenStream { - match type_name { - name if name == NULL => quote!(()), - name if name == BOOLEAN => quote!(bool), - name if name == INTEGER => { - match first_item { - Some(ASN1Value::LinkedIntValue { integer_type, .. }) => { - integer_type.to_token_stream() + pub(crate) fn format_sequence_or_set_of_item_type( + &self, + type_name: String, + first_item: Option<&ASN1Value>, + ) -> TokenStream { + match type_name { + name if name == NULL => quote!(()), + name if name == BOOLEAN => quote!(bool), + name if name == INTEGER => { + match first_item { + Some(ASN1Value::LinkedIntValue { integer_type, .. }) => { + integer_type.to_token_stream() + } + _ => quote!(Integer), // best effort } - _ => quote!(Integer), // best effort } + name if name == BIT_STRING => quote!(BitString), + name if name == OCTET_STRING => quote!(OctetString), + name if name == GENERALIZED_TIME => quote!(GeneralizedTime), + name if name == UTC_TIME => quote!(UtcTime), + name if name == OBJECT_IDENTIFIER => quote!(ObjectIdentifier), + name if name == NUMERIC_STRING => quote!(NumericString), + name if name == VISIBLE_STRING => quote!(VisibleString), + name if name == IA5_STRING => quote!(IA5String), + name if name == UTF8_STRING => quote!(UTF8String), + name if name == BMP_STRING => quote!(BMPString), + name if name == PRINTABLE_STRING => quote!(PrintableString), + name if name == GENERAL_STRING => quote!(GeneralString), + name => self.to_rust_title_case(&name), } - name if name == BIT_STRING => quote!(BitString), - name if name == OCTET_STRING => quote!(OctetString), - name if name == GENERALIZED_TIME => quote!(GeneralizedTime), - name if name == UTC_TIME => quote!(UtcTime), - name if name == OBJECT_IDENTIFIER => quote!(ObjectIdentifier), - name if name == NUMERIC_STRING => quote!(NumericString), - name if name == VISIBLE_STRING => quote!(VisibleString), - name if name == IA5_STRING => quote!(IA5String), - name if name == UTF8_STRING => quote!(UTF8String), - name if name == BMP_STRING => quote!(BMPString), - name if name == PRINTABLE_STRING => quote!(PrintableString), - name if name == GENERAL_STRING => quote!(GeneralString), - name => to_rust_title_case(&name), } -} -/// Resolves the custom syntax declared in an information object class' WITH SYNTAX clause -pub fn resolve_standard_syntax( - class: &InformationObjectClass, - application: &[InformationObjectField], -) -> Result<(ASN1Value, Vec<(usize, ASN1Type)>), GeneratorError> { - let mut key = None; - let mut field_index_map = Vec::<(usize, ASN1Type)>::new(); + /// Resolves the custom syntax declared in an information object class' WITH SYNTAX clause + pub(crate) fn resolve_standard_syntax( + &self, + class: &InformationObjectClass, + application: &[InformationObjectField], + ) -> Result<(ASN1Value, Vec<(usize, ASN1Type)>), GeneratorError> { + let mut key = None; + let mut field_index_map = Vec::<(usize, ASN1Type)>::new(); - let key_index = class - .fields - .iter() - .enumerate() - .find_map(|(i, f)| f.is_unique.then_some(i)) - .ok_or_else(|| GeneratorError { - details: format!("Could not find key for class {class:?}"), - kind: GeneratorErrorType::MissingClassKey, - ..Default::default() - })?; + let key_index = class + .fields + .iter() + .enumerate() + .find_map(|(i, f)| f.is_unique.then_some(i)) + .ok_or_else(|| GeneratorError { + details: format!("Could not find key for class {class:?}"), + kind: GeneratorErrorType::MissingClassKey, + ..Default::default() + })?; - let mut appl_iter = application.iter().enumerate(); - 'syntax_matching: for class_field in &class.fields { - if let Some((index, field)) = appl_iter.next() { - if class_field.identifier.identifier() == field.identifier() { - match field { - InformationObjectField::TypeField(f) => { - field_index_map.push((index, f.ty.clone())); - } - InformationObjectField::FixedValueField(f) => { - if index == key_index { - key = Some(f.value.clone()); + let mut appl_iter = application.iter().enumerate(); + 'syntax_matching: for class_field in &class.fields { + if let Some((index, field)) = appl_iter.next() { + if class_field.identifier.identifier() == field.identifier() { + match field { + InformationObjectField::TypeField(f) => { + field_index_map.push((index, f.ty.clone())); + } + InformationObjectField::FixedValueField(f) => { + if index == key_index { + key = Some(f.value.clone()); + } } + InformationObjectField::ObjectSetField(_) => todo!(), } - InformationObjectField::ObjectSetField(_) => todo!(), + } else if !class_field.is_optional { + return Err(GeneratorError { + top_level_declaration: None, + details: "Syntax mismatch while resolving information object.".to_string(), + kind: GeneratorErrorType::SyntaxMismatch, + }); + } else { + continue 'syntax_matching; + } + } + } + field_index_map.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); + let types = field_index_map.into_iter().collect(); + match key { + Some(k) => Ok((k, types)), + None => Err(GeneratorError { + top_level_declaration: None, + details: "Could not find class key!".into(), + kind: GeneratorErrorType::MissingClassKey, + }), + } + } + + const RUST_KEYWORDS: [&str; 38] = [ + "as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum", + "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", + "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait", + "true", "type", "unsafe", "use", "where", "while", + ]; + + pub(crate) fn to_rust_snake_case(&self, input: &str) -> Ident { + let input = input.replace('-', "_"); + let mut lowercase = String::with_capacity(input.len()); + + let peekable = &mut input.chars().peekable(); + while let Some(c) = peekable.next() { + if c.is_lowercase() || c == '_' || c.is_numeric() { + lowercase.push(c); + if peekable.peek().map_or(false, |next| next.is_uppercase()) { + lowercase.push('_'); } - } else if !class_field.is_optional { - return Err(GeneratorError { - top_level_declaration: None, - details: "Syntax mismatch while resolving information object.".to_string(), - kind: GeneratorErrorType::SyntaxMismatch, - }); } else { - continue 'syntax_matching; + lowercase.push(c.to_ascii_lowercase()); } } + let name = if Self::RUST_KEYWORDS.contains(&lowercase.as_str()) { + String::from("r_") + &lowercase + } else { + lowercase + }; + Ident::new(&name, Span::call_site()) } - field_index_map.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); - let types = field_index_map.into_iter().collect(); - match key { - Some(k) => Ok((k, types)), - None => Err(GeneratorError { - top_level_declaration: None, - details: "Could not find class key!".into(), - kind: GeneratorErrorType::MissingClassKey, - }), + + pub(crate) fn to_rust_const_case(&self, input: &str) -> Ident { + Ident::new( + &self.to_rust_snake_case(input).to_string().to_uppercase(), + Span::call_site(), + ) + } + + pub(crate) fn to_rust_enum_identifier(&self, input: &str) -> Ident { + let mut formatted = format_ident!("{}", input.replace('-', "_")); + if Self::RUST_KEYWORDS.contains(&input) { + formatted = format_ident!("R_{formatted}"); + } + formatted + } + + pub(crate) fn to_rust_title_case(&self, input: &str) -> TokenStream { + let mut input = input.replace('-', "_"); + let input = input.drain(..).fold(String::new(), |mut acc, c| { + if acc.is_empty() && c.is_lowercase() { + acc.push(c.to_ascii_uppercase()); + } else if acc.ends_with(|last: char| last == '_') && c.is_uppercase() { + acc.pop(); + acc.push(c); + } else if acc.ends_with(|last: char| last == '_') { + acc.pop(); + acc.push(c.to_ascii_uppercase()); + } else { + acc.push(c); + } + acc + }); + let name = if Self::RUST_KEYWORDS.contains(&input.as_str()) { + String::from("R_") + &input + } else { + input + }; + TokenStream::from_str(&name).unwrap() } } impl ASN1Value { - pub fn is_const_type(&self) -> bool { + pub(crate) fn is_const_type(&self) -> bool { match self { ASN1Value::Null | ASN1Value::Boolean(_) | ASN1Value::EnumeratedValue { .. } => true, ASN1Value::Choice { inner_value, .. } => inner_value.is_const_type(), @@ -915,7 +1044,7 @@ impl ASN1Value { } impl ASN1Type { - pub fn is_const_type(&self) -> bool { + pub(crate) fn is_const_type(&self) -> bool { match self { ASN1Type::Null | ASN1Type::Enumerated(_) | ASN1Type::Boolean(_) => true, ASN1Type::Integer(i) => { @@ -937,157 +1066,85 @@ impl ASN1Type { } } -#[cfg(test)] -macro_rules! assert_eq_ignore_ws { - ($left:expr, $right:expr) => { - assert_eq!( - $left.replace(|c: char| c.is_whitespace(), ""), - String::from($right).replace(|c: char| c.is_whitespace(), "") - ) - }; -} - -const RUST_KEYWORDS: [&str; 38] = [ - "as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum", "extern", - "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", - "ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", - "unsafe", "use", "where", "while", -]; - -pub fn to_rust_snake_case(input: &str) -> Ident { - let input = input.replace('-', "_"); - let mut lowercase = String::with_capacity(input.len()); - - let peekable = &mut input.chars().peekable(); - while let Some(c) = peekable.next() { - if c.is_lowercase() || c == '_' || c.is_numeric() { - lowercase.push(c); - if peekable.peek().map_or(false, |next| next.is_uppercase()) { - lowercase.push('_'); - } - } else { - lowercase.push(c.to_ascii_lowercase()); - } - } - let name = if RUST_KEYWORDS.contains(&lowercase.as_str()) { - String::from("r_") + &lowercase - } else { - lowercase - }; - Ident::new(&name, Span::call_site()) -} - -pub fn to_rust_const_case(input: &str) -> Ident { - Ident::new( - &to_rust_snake_case(input).to_string().to_uppercase(), - Span::call_site(), - ) -} - -pub fn to_rust_enum_identifier(input: &str) -> Ident { - let mut formatted = format_ident!("{}", input.replace('-', "_")); - if RUST_KEYWORDS.contains(&input) { - formatted = format_ident!("R_{formatted}"); - } - formatted -} - -pub fn to_rust_title_case(input: &str) -> TokenStream { - let mut input = input.replace('-', "_"); - let input = input.drain(..).fold(String::new(), |mut acc, c| { - if acc.is_empty() && c.is_lowercase() { - acc.push(c.to_ascii_uppercase()); - } else if acc.ends_with(|last: char| last == '_') && c.is_uppercase() { - acc.pop(); - acc.push(c); - } else if acc.ends_with(|last: char| last == '_') { - acc.pop(); - acc.push(c.to_ascii_uppercase()); - } else { - acc.push(c); - } - acc - }); - let name = if RUST_KEYWORDS.contains(&input.as_str()) { - String::from("R_") + &input - } else { - input - }; - TokenStream::from_str(&name).unwrap() -} - #[cfg(test)] mod tests { use quote::quote; - use crate::{ - generator::rasn::utils::format_tag, - intermediate::{ - constraints::ElementSet, - types::{Boolean, Enumeral, Integer}, - AsnTag, - }, + use crate::intermediate::{ + constraints::ElementSet, + types::{Boolean, Enumeral, Integer}, + AsnTag, }; use super::*; #[test] fn determines_int_type() { - assert_eq!(int_type_token(Some(600), Some(600), false), "u16"); - assert_eq!(int_type_token(Some(0), Some(0), false), "u8"); - assert_eq!(int_type_token(Some(-1), Some(1), false), "i8"); + let generator = Rasn::default(); + assert_eq!(generator.int_type_token(Some(600), Some(600), false), "u16"); + assert_eq!(generator.int_type_token(Some(0), Some(0), false), "u8"); + assert_eq!(generator.int_type_token(Some(-1), Some(1), false), "i8"); assert_eq!( - int_type_token(Some(0), Some(124213412341389457931857915125), false), + generator.int_type_token(Some(0), Some(124213412341389457931857915125), false), "Integer" ); - assert_eq!(int_type_token(Some(-67463), Some(23123), false), "i32"); - assert_eq!(int_type_token(Some(255), Some(257), false), "u16"); + assert_eq!( + generator.int_type_token(Some(-67463), Some(23123), false), + "i32" + ); + assert_eq!(generator.int_type_token(Some(255), Some(257), false), "u16"); } #[test] fn joins_annotations() { + let generator = Rasn::default(); + assert_eq_ignore_ws!( - join_annotations(vec![ - quote!(delegate), - format_tag( - Some(&AsnTag { - tag_class: crate::intermediate::TagClass::Application, - environment: crate::intermediate::TaggingEnvironment::Explicit, - id: 3, - }), - false, - ), - ]) - .to_string(), + generator + .join_annotations(vec![ + quote!(delegate), + generator.format_tag( + Some(&AsnTag { + tag_class: crate::intermediate::TagClass::Application, + environment: crate::intermediate::TaggingEnvironment::Explicit, + id: 3, + }), + false, + ), + ]) + .to_string(), "#[rasn(delegate, tag(explicit(application, 3)))]" ) } #[test] fn formats_sequence_members() { + let generator = Rasn::default(); + assert_eq_ignore_ws!( - format_sequence_or_set_members( - &SequenceOrSet { - components_of: vec![], - extensible: Some(1), - constraints: vec![], - members: vec![ - SequenceOrSetMember { - name: "testMember0".into(), - tag: None, - ty: ASN1Type::Boolean(Boolean { + generator + .format_sequence_or_set_members( + &SequenceOrSet { + components_of: vec![], + extensible: Some(1), + constraints: vec![], + members: vec![ + SequenceOrSetMember { + name: "testMember0".into(), + tag: None, + ty: ASN1Type::Boolean(Boolean { + constraints: vec![] + }), + default_value: None, + is_optional: true, constraints: vec![] - }), - default_value: None, - is_optional: true, - constraints: vec![] - }, - SequenceOrSetMember { - name: "testMember1".into(), - tag: None, - ty: ASN1Type::Integer(Integer { - distinguished_values: None, - constraints: vec![Constraint::SubtypeConstraint(ElementSet { + }, + SequenceOrSetMember { + name: "testMember1".into(), + tag: None, + ty: ASN1Type::Integer(Integer { + distinguished_values: None, + constraints: vec![Constraint::SubtypeConstraint(ElementSet { extensible: false, set: crate::intermediate::constraints::ElementOrSetOperation::Element( crate::intermediate::constraints::SubtypeElement::SingleValue { @@ -1096,18 +1153,18 @@ mod tests { } ) })] - }), - default_value: Some(ASN1Value::Integer(4)), - is_optional: true, - constraints: vec![] - } - ] - }, - &"Parent".into(), - ) - .unwrap() - .0 - .to_string(), + }), + default_value: Some(ASN1Value::Integer(4)), + is_optional: true, + constraints: vec![] + } + ] + }, + &"Parent".into(), + ) + .unwrap() + .0 + .to_string(), r#" #[rasn(identifier = "testMember0")] pub test_member0: Option, @@ -1119,29 +1176,32 @@ mod tests { #[test] fn formats_enum_members() { + let generator = Rasn::default(); + assert_eq_ignore_ws!( - format_enum_members(&Enumerated { - members: vec![ - Enumeral { - name: "test-option-1".into(), - description: Some("optional comment".into()), - index: 0 - }, - Enumeral { - name: "test-option-2".into(), - description: Some("another optional comment".into()), - index: 2 - }, - Enumeral { - name: "test-option-3".into(), - description: None, - index: 5 - } - ], - extensible: Some(2), - constraints: vec![] - }) - .to_string(), + generator + .format_enum_members(&Enumerated { + members: vec![ + Enumeral { + name: "test-option-1".into(), + description: Some("optional comment".into()), + index: 0 + }, + Enumeral { + name: "test-option-2".into(), + description: Some("another optional comment".into()), + index: 2 + }, + Enumeral { + name: "test-option-3".into(), + description: None, + index: 5 + } + ], + extensible: Some(2), + constraints: vec![] + }) + .to_string(), r#" #[rasn(identifier="test-option-1")] test_option_1=0, @@ -1155,8 +1215,10 @@ mod tests { #[test] fn formats_choice_options() { + let generator = Rasn::default(); + assert_eq_ignore_ws!( - format_choice_options( + generator.format_choice_options( &Choice { extensible: Some(1), constraints: vec![], @@ -1202,69 +1264,80 @@ mod tests { #[test] fn formats_linked_value() { + let generator = Rasn::default(); + assert_eq!( - value_to_tokens( - &ASN1Value::LinkedNestedValue { - supertypes: vec!["Outer".into(), "Inner".into()], - value: Box::new(ASN1Value::Boolean(true)) - }, - None - ) - .unwrap() - .to_string(), + generator + .value_to_tokens( + &ASN1Value::LinkedNestedValue { + supertypes: vec!["Outer".into(), "Inner".into()], + value: Box::new(ASN1Value::Boolean(true)) + }, + None + ) + .unwrap() + .to_string(), "Outer (Inner (true))" ) } #[test] fn formats_identifier_annotation() { + let generator = Rasn::default(); + assert_eq!( - format_identifier_annotation( - "original-name", - "", - &ASN1Type::Boolean(Boolean::default()) - ) - .to_string(), + generator + .format_identifier_annotation( + "original-name", + "", + &ASN1Type::Boolean(Boolean::default()) + ) + .to_string(), quote!(identifier = "original-name").to_string() ); assert_eq!( - format_identifier_annotation( - "ext_group_original-name", - "", - &ASN1Type::Boolean(Boolean::default()) - ) - .to_string(), + generator + .format_identifier_annotation( + "ext_group_original-name", + "", + &ASN1Type::Boolean(Boolean::default()) + ) + .to_string(), quote!(identifier = "BOOLEAN").to_string() ); assert_eq!( - format_identifier_annotation( - "original-name", - " Anonymous ", - &ASN1Type::Boolean(Boolean::default()) - ) - .to_string(), + generator + .format_identifier_annotation( + "original-name", + " Anonymous ", + &ASN1Type::Boolean(Boolean::default()) + ) + .to_string(), quote!(identifier = "BOOLEAN").to_string() ); assert_eq!( - format_identifier_annotation( - "original-name", - " Inner type ", - &ASN1Type::Boolean(Boolean::default()) - ) - .to_string(), + generator + .format_identifier_annotation( + "original-name", + " Inner type ", + &ASN1Type::Boolean(Boolean::default()) + ) + .to_string(), quote!(identifier = "BOOLEAN").to_string() ); } #[test] fn converts_to_snake_case() { - assert_eq!(to_rust_snake_case("HelloWorld"), "hello_world"); - assert_eq!(to_rust_snake_case("helloWorld"), "hello_world"); - assert_eq!(to_rust_snake_case("hello-world"), "hello_world"); - assert_eq!(to_rust_snake_case("HELLOWORLD"), "helloworld"); - assert_eq!(to_rust_snake_case("HelloWORLD"), "hello_world"); - assert_eq!(to_rust_snake_case("HELLO-WORLD"), "hello__world"); - assert_eq!(to_rust_snake_case("struct"), "r_struct"); - assert_eq!(to_rust_snake_case("STRUCT"), "r_struct"); + let generator = Rasn::default(); + + assert_eq!(generator.to_rust_snake_case("HelloWorld"), "hello_world"); + assert_eq!(generator.to_rust_snake_case("helloWorld"), "hello_world"); + assert_eq!(generator.to_rust_snake_case("hello-world"), "hello_world"); + assert_eq!(generator.to_rust_snake_case("HELLOWORLD"), "helloworld"); + assert_eq!(generator.to_rust_snake_case("HelloWORLD"), "hello_world"); + assert_eq!(generator.to_rust_snake_case("HELLO-WORLD"), "hello__world"); + assert_eq!(generator.to_rust_snake_case("struct"), "r_struct"); + assert_eq!(generator.to_rust_snake_case("STRUCT"), "r_struct"); } } diff --git a/rasn-compiler/src/lib.rs b/rasn-compiler/src/lib.rs index f7546a1..f89fb51 100644 --- a/rasn-compiler/src/lib.rs +++ b/rasn-compiler/src/lib.rs @@ -11,23 +11,40 @@ //! // build.rs build script //! use std::path::PathBuf; //! use rasn_compiler::prelude::*; +//! use proc_macro2::TokenStream; //! //! fn main() { +//! #[derive(Default)] //! struct CustomBackend; //! //! impl Backend for CustomBackend { +//! type Config = (); +//! //! fn generate_module( //! &self, //! top_level_declarations: Vec, //! ) -> Result { //! Ok(GeneratedModule::empty()) //! } +//! +//! fn generate( +//! &self, +//! tld: ToplevelDefinition +//! ) -> Result { +//! Ok(TokenStream::new()) +//! } +//! +//! fn config(&self) -> &Self::Config { +//! &() +//! } +//! +//! fn from_config(config: Self::Config) -> Self { +//! CustomBackend +//! } //! } //! //! // Initialize the compiler -//! match Compiler::new() -//! // optionally provide a custom backend -//! .with_backend(CustomBackend) +//! match Compiler::::new() //! // add a single ASN1 source file //! .add_asn_by_path(PathBuf::from("spec_1.asn")) //! // add several ASN1 source files @@ -64,7 +81,7 @@ use std::{ vec, }; -use generator::{rasn::Rust, Backend}; +use generator::Backend; use intermediate::ToplevelDefinition; use parser::asn_spec; use validator::Validator; @@ -76,7 +93,12 @@ pub mod prelude { CompileResult, Compiler, CompilerMissingParams, CompilerOutputSet, CompilerReady, CompilerSourcesSet, }; - pub use crate::generator::{error::*, Backend, GeneratedModule}; + pub use crate::generator::{ + error::*, + rasn::{Config as RasnConfig, Rasn as RasnBackend}, + Backend, GeneratedModule, + }; + pub use crate::intermediate::ToplevelDefinition; pub mod ir { pub use crate::intermediate::{ @@ -178,9 +200,9 @@ enum AsnSource { Literal(String), } -impl Default for Compiler { +impl Default for Compiler { fn default() -> Self { - Compiler::new() + Self::new() } } @@ -193,12 +215,20 @@ impl Compiler { } } -impl Compiler { +impl Compiler { + /// Provides a Builder for building rasn compiler commands + pub fn new() -> Compiler { + Compiler { + state: CompilerMissingParams, + backend: B::default(), + } + } + /// Provides a Builder for building rasn compiler commands - pub fn new() -> Compiler { + pub fn new_with_config(config: B::Config) -> Compiler { Compiler { state: CompilerMissingParams, - backend: Rust, + backend: B::from_config(config), } } } @@ -237,8 +267,8 @@ impl Compiler { /// Add a literal ASN1 source to the compile command /// * `literal` - literal ASN1 statement to include /// ```rust - /// # use rasn_compiler::Compiler; - /// Compiler::new().add_asn_literal(format!( + /// # use rasn_compiler::prelude::*; + /// Compiler::::new().add_asn_literal(format!( /// "TestModule DEFINITIONS AUTOMATIC TAGS::= BEGIN {} END", /// "My-test-integer ::= INTEGER (1..128)" /// )).compile_to_string(); @@ -303,8 +333,8 @@ impl Compiler { /// Add a literal ASN1 source to the compile command /// * `literal` - literal ASN1 statement to include /// ```rust - /// # use rasn_compiler::Compiler; - /// Compiler::new().add_asn_literal(format!( + /// # use rasn_compiler::prelude::*; + /// Compiler::::new().add_asn_literal(format!( /// "TestModule DEFINITIONS AUTOMATIC TAGS::= BEGIN {} END", /// "My-test-integer ::= INTEGER (1..128)" /// )).compile_to_string(); @@ -352,8 +382,8 @@ impl Compiler { /// Add a literal ASN1 source to the compile command /// * `literal` - literal ASN1 statement to include /// ```rust - /// # use rasn_compiler::Compiler; - /// Compiler::new().add_asn_literal(format!( + /// # use rasn_compiler::prelude::*; + /// Compiler::::new().add_asn_literal(format!( /// "TestModule DEFINITIONS AUTOMATIC TAGS::= BEGIN {} END", /// "My-test-integer ::= INTEGER (1..128)" /// )).compile_to_string(); @@ -481,8 +511,8 @@ impl Compiler { /// Add a literal ASN1 source to the compile command /// * `literal` - literal ASN1 statement to include /// ```rust - /// # use rasn_compiler::Compiler; - /// Compiler::new().add_asn_literal(format!( + /// # use rasn_compiler::prelude::*; + /// Compiler::::new().add_asn_literal(format!( /// "TestModule DEFINITIONS AUTOMATIC TAGS::= BEGIN {} END", /// "My-test-integer ::= INTEGER (1..128)" /// )).compile_to_string(); diff --git a/rasn-compiler/src/validator/linking/mod.rs b/rasn-compiler/src/validator/linking/mod.rs index 57d7025..01028bb 100644 --- a/rasn-compiler/src/validator/linking/mod.rs +++ b/rasn-compiler/src/validator/linking/mod.rs @@ -25,7 +25,7 @@ use self::{ utils::{built_in_type, find_tld_or_enum_value_by_name, octet_string_to_bit_string}, }; -use super::{Constraint, Parameter, TableConstraint}; +use super::{error::ValidatorError, Constraint, Parameter, TableConstraint}; macro_rules! error { ($kind:ident, $($arg:tt)*) => { @@ -39,8 +39,82 @@ macro_rules! error { impl ToplevelDefinition { pub(crate) fn is_parameterized(&self) -> bool { match self { - ToplevelDefinition::Type(t) => t.parameterization.is_some(), - _ => false, // TODO: implement parameterization for information objects and values + ToplevelDefinition::Information(ToplevelInformationDefinition { + parameterization: Some(_), + .. + }) + | ToplevelDefinition::Type(ToplevelTypeDefinition { + parameterization: Some(_), + .. + }) + | ToplevelDefinition::Value(ToplevelValueDefinition { + parameterization: Some(_), + .. + }) => true, + ToplevelDefinition::Type(ToplevelTypeDefinition { + ty: ASN1Type::Sequence(s), + .. + }) + | ToplevelDefinition::Type(ToplevelTypeDefinition { + ty: ASN1Type::Set(s), + .. + }) => s.members.iter().any(|m| { + m.constraints + .iter() + .any(|c| matches!(c, Constraint::Parameter(_))) + }), + ToplevelDefinition::Type(ToplevelTypeDefinition { + ty: ASN1Type::SequenceOf(s), + .. + }) + | ToplevelDefinition::Type(ToplevelTypeDefinition { + ty: ASN1Type::SetOf(s), + .. + }) => s.element_type.constraints().map_or(false, |constraints| { + constraints + .iter() + .any(|c| matches!(c, Constraint::Parameter(_))) + }), + _ => false, + } + } + + pub(crate) fn resolve_parameterization( + &mut self, + tlds: &BTreeMap, + ) -> Result<(), ValidatorError> { + match self { + ToplevelDefinition::Type(ToplevelTypeDefinition { + name, + ty: ASN1Type::Sequence(s), + .. + }) + | ToplevelDefinition::Type(ToplevelTypeDefinition { + name, + ty: ASN1Type::Set(s), + .. + }) => s.members.iter_mut().try_for_each(|m| { + if let Some(replacement) = m.ty.link_constraint_reference(&name, tlds)? { + m.ty = replacement; + } + Ok(()) + }), + ToplevelDefinition::Type(ToplevelTypeDefinition { + name, + ty: ASN1Type::SequenceOf(s), + .. + }) + | ToplevelDefinition::Type(ToplevelTypeDefinition { + name, + ty: ASN1Type::SetOf(s), + .. + }) => { + if let Some(replacement) = s.element_type.link_constraint_reference(name, tlds)? { + s.element_type = Box::new(replacement); + } + Ok(()) + } + _ => Ok(()), } } @@ -385,6 +459,19 @@ impl ASN1Type { } } } + ASN1Type::InformationObjectFieldReference(iofr) => { + if let Some(ToplevelDefinition::Information(ToplevelInformationDefinition { + value: ASN1Information::ObjectClass(clazz), + .. + })) = tlds.get(&iofr.class) + { + if let Some(InformationObjectClassField { ty: Some(ty), .. }) = + clazz.get_field(&iofr.field_path) + { + self_replacement = Some(ty.clone()); + } + } + } ty => { if let Some(c) = ty.constraints_mut() { for c in c.iter_mut() { @@ -588,7 +675,9 @@ impl ASN1Type { })) = tlds.get(&iofr.class) { if let Some(field) = c.get_field(&iofr.field_path) { - *self = field.ty.clone().unwrap_or(ASN1Type::External); + if let Some(ref ty) = field.ty { + *self = ty.clone(); + } return Ok(()); } } diff --git a/rasn-compiler/src/validator/linking/utils.rs b/rasn-compiler/src/validator/linking/utils.rs index 03f0c93..71262e3 100644 --- a/rasn-compiler/src/validator/linking/utils.rs +++ b/rasn-compiler/src/validator/linking/utils.rs @@ -1,9 +1,12 @@ use std::collections::BTreeMap; -use crate::intermediate::{ - error::{GrammarError, GrammarErrorType}, - information_object::*, - *, +use crate::{ + intermediate::{ + error::{GrammarError, GrammarErrorType}, + information_object::*, + *, + }, + parser::asn1_value, }; use self::types::*; @@ -144,6 +147,28 @@ pub fn resolve_custom_syntax( ty: ASN1Type::ElsewhereDeclaredType(t.clone()), }), )); + } else if let Some(index) = + class.fields.iter().enumerate().find_map(|(i, v)| { + (v.identifier + == ObjectFieldIdentifier::SingleValue( + token.name_or_empty().to_owned(), + )) + .then_some(i) + }) + { + unsorted_default_syntax.push(( + index, + InformationObjectField::FixedValueField(FixedValueField { + identifier: token.name_or_empty().to_owned(), + value: match asn1_value(&t.identifier) { + Ok((_, v)) => Ok(v), + Err(e) => Err(GrammarError { + details: format!("Syntax mismatch while resolving information object: {e:?}"), + kind: GrammarErrorType::SyntaxMismatch, + }), + }?, + }), + )); } } SyntaxApplication::TypeReference(t) => { diff --git a/rasn-compiler/src/validator/mod.rs b/rasn-compiler/src/validator/mod.rs index 05cb8c7..3bd1e7f 100644 --- a/rasn-compiler/src/validator/mod.rs +++ b/rasn-compiler/src/validator/mod.rs @@ -68,57 +68,64 @@ impl Validator { } )) ] { - let mut item = self.tlds.remove(&key); - if let Some(ToplevelDefinition::Information(ToplevelInformationDefinition { - value: ASN1Information::ObjectSet(set), - .. - })) = &mut item + let mut item = self.tlds.remove_entry(&key); + if let Some(( + _, + ToplevelDefinition::Information(ToplevelInformationDefinition { + value: ASN1Information::ObjectSet(set), + .. + }), + )) = &mut item { if let Err(e) = set.resolve_object_set_references(&self.tlds) { warnings.push(Box::new(e)) } } - if let Some(tld) = item { - self.tlds.insert(tld.name().clone(), tld); + if let Some((k, tld)) = item { + self.tlds.insert(k, tld); } } if self.references_class_by_name(&key) { - match self.tlds.remove(&key) { - Some(ToplevelDefinition::Type(mut tld)) => { + match self.tlds.remove_entry(&key) { + Some((k, ToplevelDefinition::Type(mut tld))) => { tld.ty = tld.ty.resolve_class_reference(&self.tlds); - self.tlds - .insert(tld.name.clone(), ToplevelDefinition::Type(tld)); + self.tlds.insert(k, ToplevelDefinition::Type(tld)); } - Some(ToplevelDefinition::Information(mut tld)) => { + Some((k, ToplevelDefinition::Information(mut tld))) => { tld = tld.resolve_class_reference(&self.tlds); - self.tlds - .insert(tld.name.clone(), ToplevelDefinition::Information(tld)); + self.tlds.insert(k, ToplevelDefinition::Information(tld)); } _ => (), } } + // if self.is_parameterized(&key) { + // if let Some((k, mut tld)) = self.tlds.remove_entry(&key) { + // if let Err(e) = tld.resolve_parameterization(&self.tlds) { + // warnings.push(Box::new(e)); + // } + // self.tlds.insert(k, tld); + // } + // } if self.has_components_of_notation(&key) { - if let Some(ToplevelDefinition::Type(mut tld)) = self.tlds.remove(&key) { + if let Some((k, ToplevelDefinition::Type(mut tld))) = self.tlds.remove_entry(&key) { tld.ty.link_components_of_notation(&self.tlds); - self.tlds - .insert(tld.name.clone(), ToplevelDefinition::Type(tld)); + self.tlds.insert(k, ToplevelDefinition::Type(tld)); } } if self.has_choice_selection_type(&key) { - if let Some(ToplevelDefinition::Type(mut tld)) = self.tlds.remove(&key) { + if let Some((k, ToplevelDefinition::Type(mut tld))) = self.tlds.remove_entry(&key) { if let Err(e) = tld.ty.link_choice_selection_type(&self.tlds) { warnings.push(Box::new(e)); } - self.tlds - .insert(tld.name.clone(), ToplevelDefinition::Type(tld)); + self.tlds.insert(k, ToplevelDefinition::Type(tld)); } } if self.references_object_set_by_name(&key) { - // TODO: Replace self.tlds.remove with remove entry to retrieve key string for reinsertion later - if let Some(ToplevelDefinition::Information(mut tld)) = self.tlds.remove(&key) { + if let Some((k, ToplevelDefinition::Information(mut tld))) = + self.tlds.remove_entry(&key) + { tld.value.link_object_set_reference(&self.tlds); - self.tlds - .insert(tld.name.clone(), ToplevelDefinition::Information(tld)); + self.tlds.insert(k, ToplevelDefinition::Information(tld)); } } if self.has_constraint_reference(&key) { @@ -214,8 +221,8 @@ impl Validator { } } for mut import in associated_type_imports { - if let Some(mod_imports) = mod_ref - .borrow_mut() + let mut mut_mod_ref = mod_ref.borrow_mut(); + if let Some(mod_imports) = mut_mod_ref .imports .iter_mut() .find(|i| i.global_module_reference == import.global_module_reference) @@ -224,7 +231,7 @@ impl Validator { mod_imports.types.push(std::mem::take(&mut import.types[0])); } } else { - mod_ref.borrow_mut().imports.push(import); + mut_mod_ref.imports.push(import); } } @@ -308,11 +315,7 @@ impl Validator { fn has_constraint_reference(&mut self, key: &String) -> bool { if let Some(tld) = self.tlds.get(key) { - if tld.is_parameterized() { - false - } else { - tld.has_constraint_reference() - } + tld.is_parameterized() || tld.has_constraint_reference() } else { false }