├── .gitignore ├── src ├── descriptor │ ├── common.rs │ ├── optional.rs │ ├── complex.rs │ ├── set.rs │ ├── setof.rs │ ├── boolean.rs │ ├── enumerated.rs │ ├── default.rs │ ├── ia5string.rs │ ├── utf8string.rs │ ├── octetstring.rs │ ├── numericstring.rs │ ├── visiblestring.rs │ ├── choice.rs │ ├── printablestring.rs │ ├── sequenceof.rs │ ├── sequence.rs │ ├── null.rs │ ├── numbers.rs │ └── bitstring.rs ├── prelude.rs ├── rw │ └── mod.rs ├── protocol │ ├── mod.rs │ ├── basic │ │ ├── mod.rs │ │ └── err.rs │ └── protobuf │ │ └── peq.rs ├── lib.rs ├── internal_macros.rs ├── main.rs └── converter.rs ├── asn1rs-macros ├── README.md ├── Cargo.toml └── src │ ├── lib.rs │ └── derive_protobuf_eq.rs ├── asn1rs-model ├── README.md ├── src │ ├── lib.rs │ ├── parse │ │ ├── location.rs │ │ ├── token.rs │ │ └── tokenizer.rs │ ├── generate │ │ └── mod.rs │ ├── proc_macro │ │ ├── constants.rs │ │ ├── inline.rs │ │ ├── tag.rs │ │ ├── size.rs │ │ └── range.rs │ ├── asn │ │ ├── oid.rs │ │ ├── range.rs │ │ ├── bit_string.rs │ │ ├── components.rs │ │ ├── integer.rs │ │ ├── inner_type_constraints.rs │ │ ├── tag_resolver.rs │ │ ├── charset.rs │ │ ├── peekable.rs │ │ ├── enumerated.rs │ │ └── size.rs │ └── resolve.rs └── Cargo.toml ├── tests ├── uper_octet_string_fragmented_16383.uper ├── uper_octet_string_fragmented_16384.uper ├── uper_octet_string_fragmented_65536.uper ├── empty_definition.rs ├── inner_tag_resolver.rs ├── protobuf_bools.rs ├── test_etsi.rs ├── value_reference_wrapped.rs ├── protobuf_numbers.rs ├── protobuf_strings.rs ├── protobuf_enumerated_missing.rs ├── protobuf_set.rs ├── protobuf_sequence.rs ├── uper_octet_string.rs ├── basic_null.rs ├── parse_with_components.rs ├── protobuf_optionals.rs ├── resolve_enumerated.rs ├── basic_sequence.rs ├── der_basic_boolean.rs ├── der_basic_number.rs ├── basic_integer.rs ├── default_boolean.rs ├── default_string.rs ├── protobuf_enumerated.rs ├── default_integer.rs ├── basic_sequence_of.rs ├── derive_protobuf_eq.rs ├── der_print_container.rs ├── showcase.rs ├── protobuf_set_of.rs ├── octet_string.rs ├── basic_set_of.rs ├── basic_enumerated.rs ├── extensible_integer.rs ├── basic_numeric_string.rs ├── basic_bitstring.rs ├── basic_ia5string.rs ├── basic_set.rs ├── protobuf_choice.rs ├── protobuf_sequence_of.rs ├── basic_printable_string.rs ├── basic_visible_string.rs ├── test_utils.rs ├── basic_utf8string.rs └── proc_macro_coverage_hack.rs ├── LICENSE-MIT └── Cargo.toml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *iml 4 | -------------------------------------------------------------------------------- /src/descriptor/common.rs: -------------------------------------------------------------------------------- 1 | use asn1rs_model::asn::Tag; 2 | 3 | pub trait Constraint { 4 | const TAG: Tag; 5 | } 6 | -------------------------------------------------------------------------------- /asn1rs-macros/README.md: -------------------------------------------------------------------------------- 1 | # asn1rs macros 2 | 3 | Optional part of [asn1rs](https://crates.io/crates/asn1rs) 4 | 5 | 6 | -------------------------------------------------------------------------------- /asn1rs-model/README.md: -------------------------------------------------------------------------------- 1 | # asn1rs type definitions 2 | 3 | Essential part of [asn1rs](https://crates.io/crates/asn1rs) 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/uper_octet_string_fragmented_16383.uper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellerkindt/asn1rs/HEAD/tests/uper_octet_string_fragmented_16383.uper -------------------------------------------------------------------------------- /tests/uper_octet_string_fragmented_16384.uper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellerkindt/asn1rs/HEAD/tests/uper_octet_string_fragmented_16384.uper -------------------------------------------------------------------------------- /tests/uper_octet_string_fragmented_65536.uper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellerkindt/asn1rs/HEAD/tests/uper_octet_string_fragmented_65536.uper -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::descriptor::prelude::*; 2 | #[cfg(feature = "macros")] 3 | pub use crate::macros::*; 4 | #[cfg(feature = "protobuf")] 5 | pub use crate::protocol::protobuf::ProtobufEq; 6 | pub use crate::protocol::*; 7 | pub use crate::rw::*; 8 | -------------------------------------------------------------------------------- /asn1rs-model/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate strum_macros; 3 | 4 | #[cfg(feature = "protobuf")] 5 | pub mod protobuf; 6 | 7 | pub mod asn; 8 | pub mod generate; 9 | pub mod parse; 10 | pub mod proc_macro; 11 | pub mod resolve; 12 | pub mod rust; 13 | 14 | mod model; 15 | 16 | pub use model::*; 17 | -------------------------------------------------------------------------------- /src/rw/mod.rs: -------------------------------------------------------------------------------- 1 | mod der; 2 | mod println; 3 | #[cfg(feature = "protobuf")] 4 | mod proto_read; 5 | #[cfg(feature = "protobuf")] 6 | mod proto_write; 7 | mod uper; 8 | 9 | pub use der::*; 10 | pub use println::*; 11 | #[cfg(feature = "protobuf")] 12 | pub use proto_read::*; 13 | #[cfg(feature = "protobuf")] 14 | pub use proto_write::*; 15 | pub use uper::*; 16 | -------------------------------------------------------------------------------- /tests/empty_definition.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | use asn1rs::prelude::asn_to_rust; 4 | 5 | // this should compile without noise 6 | asn_to_rust!( 7 | r"SomeEmptyTypeDefinitions DEFINITIONS AUTOMATIC TAGS ::= 8 | BEGIN 9 | 10 | EmptySeq ::= SEQUENCE { } 11 | 12 | ChoiceWithEmptyness ::= CHOICE { 13 | c1 INTEGER, 14 | c2 SEQUENCE {} 15 | } 16 | 17 | END" 18 | ); 19 | -------------------------------------------------------------------------------- /tests/inner_tag_resolver.rs: -------------------------------------------------------------------------------- 1 | use asn1rs::prelude::*; 2 | 3 | asn_to_rust!( 4 | "InnerEnumerated DEFINITIONS AUTOMATIC TAGS ::= 5 | BEGIN 6 | 7 | Other ::= SEQUENCE { 8 | field INTEGER 9 | } 10 | 11 | SomeSeq ::= SEQUENCE { 12 | content CHOICE { 13 | abc Other, 14 | def Other 15 | } 16 | } 17 | 18 | END" 19 | ); 20 | 21 | #[test] 22 | pub fn test_whether_it_compiles() {} 23 | -------------------------------------------------------------------------------- /asn1rs-model/src/parse/location.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Default, Copy, Clone, PartialOrd, PartialEq, Eq)] 2 | pub struct Location { 3 | line: usize, 4 | column: usize, 5 | } 6 | 7 | impl Location { 8 | pub const fn at(line: usize, column: usize) -> Location { 9 | Self { line, column } 10 | } 11 | 12 | pub const fn line(&self) -> usize { 13 | self.line 14 | } 15 | 16 | pub const fn column(&self) -> usize { 17 | self.column 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | //! ```text 2 | //! crate::io Utils, common io-root 3 | //! ::io::per Generic Packed Encoding impls and traits 4 | //! ::io::per::unaligned UNALIGNED PER specialization 5 | //! ::io::per::aligned ALIGNED PER specialization 6 | //! ::io::... Other ASN.1 representations (e.g der, xer, ber, ...) 7 | //! ``` 8 | 9 | pub mod basic; 10 | pub mod per; 11 | #[cfg(feature = "protobuf")] 12 | pub mod protobuf; 13 | -------------------------------------------------------------------------------- /asn1rs-model/src/generate/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "protobuf")] 2 | pub mod protobuf; 3 | pub mod rust; 4 | pub mod walker; 5 | 6 | pub use self::rust::RustCodeGenerator; 7 | 8 | use crate::model::{Model, Target}; 9 | 10 | pub trait Generator { 11 | type Error; 12 | 13 | fn add_model(&mut self, model: Model); 14 | 15 | fn models(&self) -> &[Model]; 16 | 17 | fn models_mut(&mut self) -> &mut [Model]; 18 | 19 | fn to_string(&self) -> Result, Self::Error>; 20 | } 21 | -------------------------------------------------------------------------------- /tests/protobuf_bools.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufBools ::= SEQUENCE { 10 | one-bool BOOLEAN, 11 | two-bool BOOLEAN 12 | } 13 | 14 | END" 15 | ); 16 | 17 | #[test] 18 | #[cfg(feature = "protobuf")] 19 | fn test_bools() { 20 | serialize_and_deserialize_protobuf( 21 | &[8, 1, 16, 0], 22 | &ProtobufBools { 23 | one_bool: true, 24 | two_bool: false, 25 | }, 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(rustdoc::broken_intra_doc_links)] 2 | #![warn(unused_extern_crates)] 3 | 4 | #[cfg(feature = "macros")] 5 | pub extern crate asn1rs_macros as macros; 6 | 7 | // provide an empty module, so that `use asn1rs::macros::*;` does not fail 8 | #[cfg(not(feature = "macros"))] 9 | pub mod macros {} 10 | 11 | #[macro_use] 12 | pub mod internal_macros; 13 | 14 | pub mod descriptor; 15 | pub mod prelude; 16 | pub mod protocol; 17 | pub mod rw; 18 | 19 | #[cfg(feature = "model")] 20 | pub mod converter; 21 | #[cfg(feature = "model")] 22 | pub use asn1rs_model as model; 23 | -------------------------------------------------------------------------------- /src/descriptor/optional.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | 3 | impl WritableType for Option { 4 | type Type = Option; 5 | 6 | #[inline] 7 | fn write_value( 8 | writer: &mut W, 9 | value: &Self::Type, 10 | ) -> Result<(), ::Error> { 11 | writer.write_opt::(value.as_ref()) 12 | } 13 | } 14 | 15 | impl ReadableType for Option { 16 | type Type = Option; 17 | 18 | #[inline] 19 | fn read_value(reader: &mut R) -> Result::Error> { 20 | reader.read_opt::() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/test_etsi.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"DENM-PDU-Descriptions {itu-t (0) identified-organization (4) etsi (0) itsDomain (5) wg1 (1) en (302637) denm (1) version (2) 9 | } 10 | 11 | DEFINITIONS AUTOMATIC TAGS ::= 12 | 13 | BEGIN 14 | 15 | ValidityDuration ::= INTEGER { 16 | timeOfDetection(0), 17 | oneSecondAfterDetection(1) 18 | } (0..86400) 19 | 20 | 21 | ManagementContainer ::= SEQUENCE { 22 | validityDuration ValidityDuration DEFAULT defaultValidity, 23 | ... 24 | } 25 | 26 | defaultValidity INTEGER ::= 600 27 | 28 | END 29 | " 30 | ); 31 | 32 | #[test] 33 | fn does_it_compile() {} 34 | -------------------------------------------------------------------------------- /asn1rs-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "asn1rs-macros" 3 | version = "0.4.0" 4 | authors = ["Michael Watzko "] 5 | edition = "2018" 6 | description = "Macros for asn1rs" 7 | keywords = ["proc", "macro", "asn1", "protobuf"] 8 | categories = ["parsing"] 9 | repository = "https://github.com/kellerkindt/asn1rs-proc" 10 | license = "MIT/Apache-2.0" 11 | readme = "README.md" 12 | 13 | 14 | [lib] 15 | proc-macro = true 16 | 17 | [features] 18 | default = [] 19 | debug-proc-macro = [] 20 | 21 | 22 | [dependencies] 23 | asn1rs-model = { version = "0.4.0", path = "../asn1rs-model" } 24 | syn = { version = "2.0.48", features = ["full", "visit", "extra-traits"] } 25 | quote = "1.0.35" 26 | -------------------------------------------------------------------------------- /tests/value_reference_wrapped.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r#"TransparentConsts DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | 10 | WrappedType ::= INTEGER (0..255) 11 | 12 | WrappedZero WrappedType ::= 0 13 | wrapped-one WrappedType ::= 1 14 | wrappedTwo WrappedType ::= 2 15 | Wrapped-Three WrappedType ::= 3 16 | 17 | END"# 18 | ); 19 | 20 | #[test] 21 | pub fn does_it_compile() { 22 | assert_eq!(WrappedType(0u8), WRAPPED_ZERO); 23 | assert_eq!(WrappedType(1u8), WRAPPED_ONE); 24 | assert_eq!(WrappedType(2u8), WRAPPED_TWO); 25 | assert_eq!(WrappedType(3u8), WRAPPED_THREE); 26 | } 27 | -------------------------------------------------------------------------------- /asn1rs-model/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "asn1rs-model" 3 | version = "0.4.0" 4 | authors = ["Michael Watzko "] 5 | edition = "2018" 6 | description = "Rust and Protobuf model definitions for asn1rs" 7 | keywords = ["asn1", "protobuf", "model"] 8 | categories = ["parsing"] 9 | repository = "https://github.com/kellerkindt/asn1rs" 10 | license = "MIT/Apache-2.0" 11 | readme = "README.md" 12 | 13 | [dependencies] 14 | backtrace = "0.3.69" 15 | codegen = "0.2.0" 16 | syn = {version = "2.0.48", features = ["full", "extra-traits"] } 17 | quote = "1.0.35" 18 | proc-macro2 = "1.0.76" 19 | strum = "0.25.0" 20 | strum_macros = "0.25.3" 21 | 22 | [features] 23 | default = [] 24 | protobuf = [] 25 | debug-proc-macro = [] 26 | generate-internal-docs = [] 27 | -------------------------------------------------------------------------------- /src/descriptor/complex.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{Readable, ReadableType, Reader, Writable, WritableType, Writer}; 2 | use core::marker::PhantomData; 3 | 4 | pub struct Complex(PhantomData, PhantomData); 5 | 6 | pub trait Constraint: super::common::Constraint {} 7 | 8 | impl WritableType for Complex { 9 | type Type = V; 10 | 11 | #[inline] 12 | fn write_value( 13 | writer: &mut W, 14 | value: &Self::Type, 15 | ) -> Result<(), ::Error> { 16 | value.write(writer) 17 | } 18 | } 19 | 20 | impl ReadableType for Complex { 21 | type Type = V; 22 | 23 | #[inline] 24 | fn read_value(reader: &mut R) -> Result::Error> { 25 | V::read(reader) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/descriptor/set.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use core::marker::PhantomData; 3 | 4 | pub use crate::descriptor::sequence::Constraint; 5 | 6 | pub struct Set(PhantomData); 7 | 8 | impl WritableType for Set { 9 | type Type = C; 10 | 11 | #[inline] 12 | fn write_value( 13 | writer: &mut W, 14 | value: &Self::Type, 15 | ) -> Result<(), ::Error> { 16 | writer.write_set::(|w| value.write_seq::(w)) 17 | } 18 | } 19 | 20 | impl ReadableType for Set 21 | where 22 | C: Sized, 23 | { 24 | type Type = C; 25 | 26 | #[inline] 27 | fn read_value(reader: &mut R) -> Result::Error> { 28 | reader.read_set::(C::read_seq) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /asn1rs-model/src/proc_macro/constants.rs: -------------------------------------------------------------------------------- 1 | use syn::parenthesized; 2 | use syn::parse::{Parse, ParseStream}; 3 | use syn::{Ident, Lit}; 4 | 5 | #[derive(Debug)] 6 | pub enum ConstLit { 7 | I64(String, i64), 8 | } 9 | 10 | impl Parse for ConstLit { 11 | fn parse<'a>(input: ParseStream) -> syn::Result { 12 | let name = input.parse::()?.to_string(); 13 | let content; 14 | parenthesized!(content in input); 15 | let value = content.parse::()?; 16 | match value { 17 | Lit::Int(int) => Ok(ConstLit::I64( 18 | name, 19 | int.base10_digits() 20 | .parse() 21 | .map_err(|_| syn::Error::new(int.span(), "Not an integer"))?, 22 | )), 23 | _ => Err(syn::Error::new(value.span(), "Unsupported literal")), 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/oid.rs: -------------------------------------------------------------------------------- 1 | /// The object-identifier is described in ITU-T X.680 | ISO/IEC 8824-1:2015 2 | /// in chapter 32. The XML-related definitions as well as'DefinedValue' is 3 | /// ignored by this implementation. 4 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 5 | pub struct ObjectIdentifier(pub Vec); 6 | 7 | impl ObjectIdentifier { 8 | pub fn iter(&self) -> impl Iterator { 9 | self.0.iter() 10 | } 11 | } 12 | 13 | /// The object-identifier is described in ITU-T X.680 | ISO/IEC 8824-1:2015 14 | /// in chapter 32. The XML-related definitions as well as'DefinedValue' is 15 | /// ignored by this implementation. 16 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 17 | pub enum ObjectIdentifierComponent { 18 | NameForm(String), 19 | NumberForm(u64), 20 | NameAndNumberForm(String, u64), 21 | } 22 | -------------------------------------------------------------------------------- /src/descriptor/setof.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use core::marker::PhantomData; 3 | 4 | pub use crate::descriptor::sequenceof::Constraint; 5 | pub use crate::descriptor::sequenceof::NoConstraint; 6 | 7 | pub struct SetOf(PhantomData, PhantomData); 8 | 9 | impl WritableType for SetOf { 10 | type Type = Vec; 11 | 12 | #[inline] 13 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 14 | writer.write_set_of::(value.as_slice()) 15 | } 16 | } 17 | 18 | impl ReadableType for SetOf { 19 | type Type = Vec; 20 | 21 | #[inline] 22 | fn read_value(reader: &mut R) -> Result::Error> { 23 | reader.read_set_of::() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /asn1rs-model/src/proc_macro/inline.rs: -------------------------------------------------------------------------------- 1 | use crate::generate::rust::RustCodeGenerator as RustGenerator; 2 | use crate::generate::Generator; 3 | use crate::model::Model; 4 | use crate::parse::Tokenizer; 5 | 6 | pub fn asn_to_rust(input: &str) -> String { 7 | let tokens = Tokenizer.parse(input); 8 | let model = Model::try_from(tokens) 9 | .expect("Failed to parse tokens") 10 | .try_resolve() 11 | .expect("Failed to resolve value references"); 12 | 13 | let output = RustGenerator::from(model.to_rust()) 14 | .to_string() 15 | .unwrap() 16 | .into_iter() 17 | .map(|(_file, content)| content) 18 | .collect::>() 19 | .join("\n"); 20 | 21 | if cfg!(feature = "debug-proc-macro") { 22 | println!("-------- output start"); 23 | println!("{}", output); 24 | println!("-------- output end"); 25 | } 26 | 27 | output 28 | } 29 | -------------------------------------------------------------------------------- /tests/protobuf_numbers.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufNumbers ::= SEQUENCE { 10 | should-be-sint32 INTEGER (-2147483648..2147483647), 11 | should-be-sint64-1 INTEGER (-2147483649..2147483647), 12 | should-be-sint64-2 INTEGER (-2147483648..2147483648), 13 | should-be-uint32 INTEGER (0..4294967295), 14 | should-be-uint64 INTEGER (0..4294967296) 15 | } 16 | 17 | END" 18 | ); 19 | 20 | #[test] 21 | #[cfg(feature = "protobuf")] 22 | fn test_numbers() { 23 | serialize_and_deserialize_protobuf( 24 | &[8, 2, 16, 3, 24, 6, 32, 4, 40, 5], 25 | &ProtobufNumbers { 26 | should_be_sint32: 1_i32, 27 | should_be_sint64_1: -2_i64, 28 | should_be_sint64_2: 3_i64, 29 | should_be_uint32: 4_u32, 30 | should_be_uint64: 5_u64, 31 | }, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /asn1rs-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use syn::parse_macro_input; 3 | use syn::DeriveInput; 4 | use syn::LitStr; 5 | 6 | mod derive_protobuf_eq; 7 | 8 | #[proc_macro] 9 | pub fn asn_to_rust(item: TokenStream) -> TokenStream { 10 | let input = parse_macro_input!(item as LitStr).value(); 11 | asn1rs_model::proc_macro::asn_to_rust(&input) 12 | .parse() 13 | .unwrap() 14 | } 15 | 16 | #[proc_macro_attribute] 17 | pub fn asn(attr: TokenStream, item: TokenStream) -> TokenStream { 18 | TokenStream::from(asn1rs_model::proc_macro::parse(attr.into(), item.into())) 19 | } 20 | 21 | #[proc_macro_derive(ProtobufEq)] 22 | pub fn protobuf_eq(input: TokenStream) -> TokenStream { 23 | let output = derive_protobuf_eq::expand(parse_macro_input!(input as DeriveInput)); 24 | 25 | if cfg!(feature = "debug-proc-macro") { 26 | println!("-------- output start"); 27 | println!("{}", output); 28 | println!("-------- output end"); 29 | } 30 | 31 | output 32 | } 33 | -------------------------------------------------------------------------------- /tests/protobuf_strings.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufStrings ::= SEQUENCE { 10 | utf8string UTF8String, 11 | ia5string IA5String, 12 | octetstring OCTET STRING, 13 | bitstring BIT STRING 14 | } 15 | 16 | END" 17 | ); 18 | 19 | #[test] 20 | #[cfg(feature = "protobuf")] 21 | fn test_strings() { 22 | serialize_and_deserialize_protobuf( 23 | &[ 24 | 10, 10, 117, 116, 102, 56, 115, 116, 114, 105, 110, 103, 18, 9, 105, 97, 53, 115, 116, 25 | 114, 105, 110, 103, 26, 4, 222, 173, 190, 239, 34, 10, 19, 0x36, 0, 0, 0, 0, 0, 0, 0, 26 | 15, 27 | ], 28 | &ProtobufStrings { 29 | utf8string: "utf8string".into(), 30 | ia5string: "ia5string".into(), 31 | octetstring: vec![0xDE, 0xAD, 0xBE, 0xEF], 32 | bitstring: BitVec::from_bytes(vec![0x13, 0x36], 15), 33 | }, 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /src/descriptor/boolean.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct Boolean(PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint {} 8 | 9 | #[derive(Default)] 10 | pub struct NoConstraint; 11 | impl super::common::Constraint for NoConstraint { 12 | const TAG: Tag = Tag::DEFAULT_BOOLEAN; 13 | } 14 | impl Constraint for NoConstraint {} 15 | 16 | impl WritableType for Boolean { 17 | type Type = bool; 18 | 19 | #[inline] 20 | fn write_value( 21 | writer: &mut W, 22 | value: &Self::Type, 23 | ) -> Result<(), ::Error> { 24 | writer.write_boolean::(*value) 25 | } 26 | } 27 | 28 | impl ReadableType for Boolean { 29 | type Type = bool; 30 | 31 | #[inline] 32 | fn read_value(reader: &mut R) -> Result::Error> { 33 | reader.read_boolean::() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/protobuf_enumerated_missing.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ComplexType ::= SEQUENCE { 10 | enum-one ENUMERATED { 11 | A, 12 | B, 13 | C 14 | }, 15 | enum-two ENUMERATEd { 16 | AA, 17 | BB, 18 | CC 19 | } 20 | } 21 | 22 | END" 23 | ); 24 | 25 | #[test] 26 | #[cfg(feature = "protobuf")] 27 | fn test_missing_enum_one() { 28 | assert_eq!( 29 | ComplexType { 30 | enum_one: ComplexTypeEnumOne::B, 31 | enum_two: ComplexTypeEnumTwo::Aa, 32 | }, 33 | deserialize_protobuf(&[8, 1],) 34 | ); 35 | } 36 | 37 | #[test] 38 | #[cfg(feature = "protobuf")] 39 | fn test_missing_enum_two() { 40 | assert_eq!( 41 | ComplexType { 42 | enum_one: ComplexTypeEnumOne::A, 43 | enum_two: ComplexTypeEnumTwo::Bb, 44 | }, 45 | deserialize_protobuf(&[16, 1],) 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/descriptor/enumerated.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use core::marker::PhantomData; 3 | 4 | pub struct Enumerated(PhantomData); 5 | 6 | pub trait Constraint: super::common::Constraint + Sized { 7 | const NAME: &'static str; 8 | const VARIANT_COUNT: u64; 9 | const STD_VARIANT_COUNT: u64; 10 | const EXTENSIBLE: bool = false; 11 | 12 | fn to_choice_index(&self) -> u64; 13 | 14 | fn from_choice_index(index: u64) -> Option; 15 | } 16 | 17 | impl WritableType for Enumerated { 18 | type Type = C; 19 | 20 | #[inline] 21 | fn write_value( 22 | writer: &mut W, 23 | value: &Self::Type, 24 | ) -> Result<(), ::Error> { 25 | writer.write_enumerated(value) 26 | } 27 | } 28 | 29 | impl ReadableType for Enumerated { 30 | type Type = C; 31 | 32 | #[inline] 33 | fn read_value(reader: &mut R) -> Result::Error> { 34 | reader.read_enumerated::() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/protobuf_set.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufSet ::= SET { 10 | inner SET { magic-number INTEGER } 11 | } 12 | 13 | ProtobufSetExt ::= SET { 14 | lone-bool BOOLEAN, 15 | inner SET { magic-number INTEGER }, 16 | another-string UTF8String 17 | } 18 | 19 | END" 20 | ); 21 | 22 | #[test] 23 | #[cfg(feature = "protobuf")] 24 | fn test_set() { 25 | serialize_and_deserialize_protobuf( 26 | &[10, 2, 8, 42], 27 | &ProtobufSet { 28 | inner: ProtobufSetInner { magic_number: 42 }, 29 | }, 30 | ) 31 | } 32 | 33 | #[test] 34 | #[cfg(feature = "protobuf")] 35 | fn test_set_ext() { 36 | serialize_and_deserialize_protobuf( 37 | &[8, 0, 18, 3, 8, 185, 10, 26, 3, 101, 120, 116], 38 | &ProtobufSetExt { 39 | lone_bool: false, 40 | inner: ProtobufSetExtInner { magic_number: 1337 }, 41 | another_string: "ext".into(), 42 | }, 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Michael Watzko 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/descriptor/default.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use core::marker::PhantomData; 3 | use std::fmt::Debug; 4 | 5 | pub struct DefaultValue(PhantomData, PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint { 8 | type Owned; 9 | type Borrowed: PartialEq 10 | + ToOwned::Owned> 11 | + Debug 12 | + 'static 13 | + ?Sized; 14 | 15 | const DEFAULT_VALUE: &'static Self::Borrowed; 16 | } 17 | 18 | impl> WritableType for DefaultValue { 19 | type Type = T::Type; 20 | 21 | #[inline] 22 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 23 | writer.write_default::(value) 24 | } 25 | } 26 | 27 | impl> ReadableType for DefaultValue { 28 | type Type = T::Type; 29 | 30 | #[inline] 31 | fn read_value(reader: &mut R) -> Result::Error> { 32 | reader.read_default::() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/descriptor/ia5string.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct Ia5String(PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint { 8 | const MIN: Option = None; 9 | const MAX: Option = None; 10 | const EXTENSIBLE: bool = false; 11 | } 12 | 13 | #[derive(Default)] 14 | pub struct NoConstraint; 15 | impl super::common::Constraint for NoConstraint { 16 | const TAG: Tag = Tag::DEFAULT_IA5_STRING; 17 | } 18 | impl Constraint for NoConstraint {} 19 | 20 | impl WritableType for Ia5String { 21 | type Type = String; 22 | 23 | #[inline] 24 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 25 | writer.write_ia5string::(value.as_str()) 26 | } 27 | } 28 | 29 | impl ReadableType for Ia5String { 30 | type Type = String; 31 | 32 | #[inline] 33 | fn read_value(reader: &mut R) -> Result::Error> { 34 | reader.read_ia5string::() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/descriptor/utf8string.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct Utf8String(PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint { 8 | const MIN: Option = None; 9 | const MAX: Option = None; 10 | const EXTENSIBLE: bool = false; 11 | } 12 | 13 | #[derive(Default)] 14 | pub struct NoConstraint; 15 | impl super::common::Constraint for NoConstraint { 16 | const TAG: Tag = Tag::DEFAULT_UTF8_STRING; 17 | } 18 | impl Constraint for NoConstraint {} 19 | 20 | impl WritableType for Utf8String { 21 | type Type = String; 22 | 23 | #[inline] 24 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 25 | writer.write_utf8string::(value.as_str()) 26 | } 27 | } 28 | 29 | impl ReadableType for Utf8String { 30 | type Type = String; 31 | 32 | #[inline] 33 | fn read_value(reader: &mut R) -> Result::Error> { 34 | reader.read_utf8string::() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/protobuf_sequence.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufSequence ::= SEQUENCE { 10 | inner SEQUENCE { magic-number INTEGER } 11 | } 12 | 13 | ProtobufSequenceExt ::= SEQUENCE { 14 | lone-bool BOOLEAN, 15 | inner SEQUENCE { magic-number INTEGER }, 16 | another-string UTF8String 17 | } 18 | 19 | END" 20 | ); 21 | 22 | #[test] 23 | #[cfg(feature = "protobuf")] 24 | fn test_sequence() { 25 | serialize_and_deserialize_protobuf( 26 | &[10, 2, 8, 42], 27 | &ProtobufSequence { 28 | inner: ProtobufSequenceInner { magic_number: 42 }, 29 | }, 30 | ) 31 | } 32 | 33 | #[test] 34 | #[cfg(feature = "protobuf")] 35 | fn test_sequence_ext() { 36 | serialize_and_deserialize_protobuf( 37 | &[8, 0, 18, 3, 8, 185, 10, 26, 3, 101, 120, 116], 38 | &ProtobufSequenceExt { 39 | lone_bool: false, 40 | inner: ProtobufSequenceExtInner { magic_number: 1337 }, 41 | another_string: "ext".into(), 42 | }, 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /src/descriptor/octetstring.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct OctetString(PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint { 8 | const MIN: Option = None; 9 | const MAX: Option = None; 10 | const EXTENSIBLE: bool = false; 11 | } 12 | 13 | #[derive(Default)] 14 | pub struct NoConstraint; 15 | impl super::common::Constraint for NoConstraint { 16 | const TAG: Tag = Tag::DEFAULT_OCTET_STRING; 17 | } 18 | impl Constraint for NoConstraint {} 19 | 20 | impl WritableType for OctetString { 21 | type Type = Vec; 22 | 23 | #[inline] 24 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 25 | writer.write_octet_string::(value.as_slice()) 26 | } 27 | } 28 | 29 | impl ReadableType for OctetString { 30 | type Type = Vec; 31 | 32 | #[inline] 33 | fn read_value(reader: &mut R) -> Result::Error> { 34 | reader.read_octet_string::() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/descriptor/numericstring.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct NumericString(PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint { 8 | const MIN: Option = None; 9 | const MAX: Option = None; 10 | const EXTENSIBLE: bool = false; 11 | } 12 | 13 | #[derive(Default)] 14 | pub struct NoConstraint; 15 | impl super::common::Constraint for NoConstraint { 16 | const TAG: Tag = Tag::DEFAULT_NUMERIC_STRING; 17 | } 18 | impl Constraint for NoConstraint {} 19 | 20 | impl WritableType for NumericString { 21 | type Type = String; 22 | 23 | #[inline] 24 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 25 | writer.write_numeric_string::(value.as_str()) 26 | } 27 | } 28 | 29 | impl ReadableType for NumericString { 30 | type Type = String; 31 | 32 | #[inline] 33 | fn read_value(reader: &mut R) -> Result::Error> { 34 | reader.read_numeric_string::() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/descriptor/visiblestring.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct VisibleString(PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint { 8 | const MIN: Option = None; 9 | const MAX: Option = None; 10 | const EXTENSIBLE: bool = false; 11 | } 12 | 13 | #[derive(Default)] 14 | pub struct NoConstraint; 15 | impl super::common::Constraint for NoConstraint { 16 | const TAG: Tag = Tag::DEFAULT_VISIBLE_STRING; 17 | } 18 | impl Constraint for NoConstraint {} 19 | 20 | impl WritableType for VisibleString { 21 | type Type = String; 22 | 23 | #[inline] 24 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 25 | writer.write_visible_string::(value.as_str()) 26 | } 27 | } 28 | 29 | impl ReadableType for VisibleString { 30 | type Type = String; 31 | 32 | #[inline] 33 | fn read_value(reader: &mut R) -> Result::Error> { 34 | reader.read_visible_string::() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/descriptor/choice.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use core::marker::PhantomData; 3 | 4 | pub struct Choice(PhantomData); 5 | 6 | pub trait Constraint: super::common::Constraint + Sized { 7 | const NAME: &'static str; 8 | const VARIANT_COUNT: u64; 9 | const STD_VARIANT_COUNT: u64; 10 | const EXTENSIBLE: bool = false; 11 | 12 | fn to_choice_index(&self) -> u64; 13 | 14 | fn write_content(&self, writer: &mut W) -> Result<(), W::Error>; 15 | 16 | fn read_content(index: u64, reader: &mut R) -> Result, R::Error>; 17 | } 18 | 19 | impl WritableType for Choice { 20 | type Type = C; 21 | 22 | #[inline] 23 | fn write_value( 24 | writer: &mut W, 25 | value: &Self::Type, 26 | ) -> Result<(), ::Error> { 27 | writer.write_choice(value) 28 | } 29 | } 30 | 31 | impl ReadableType for Choice { 32 | type Type = C; 33 | 34 | #[inline] 35 | fn read_value(reader: &mut R) -> Result::Error> { 36 | reader.read_choice::() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/uper_octet_string.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r#"TransparentConsts DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | Container ::= SEQUENCE { 10 | value OCTET STRING 11 | } 12 | 13 | END"# 14 | ); 15 | 16 | #[test] 17 | pub fn octet_string_fragmented_16383() { 18 | octet_string_fragmented( 19 | 16383, 20 | 8 * 16385, 21 | include_bytes!("uper_octet_string_fragmented_16383.uper"), 22 | ); 23 | } 24 | 25 | #[test] 26 | pub fn octet_string_fragmented_16384() { 27 | octet_string_fragmented( 28 | 16384, 29 | 8 * 16386, 30 | include_bytes!("uper_octet_string_fragmented_16384.uper"), 31 | ); 32 | } 33 | 34 | #[test] 35 | pub fn octet_string_fragmented_65536() { 36 | octet_string_fragmented( 37 | 65536, 38 | 8 * 65538, 39 | include_bytes!("uper_octet_string_fragmented_65536.uper"), 40 | ); 41 | } 42 | 43 | pub fn octet_string_fragmented(value_len: usize, bits: usize, bytes: &[u8]) { 44 | let container = Container { 45 | value: vec![0u8; value_len], 46 | }; 47 | serialize_and_deserialize_uper(bits, bytes, &container); 48 | } 49 | -------------------------------------------------------------------------------- /src/descriptor/printablestring.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct PrintableString(PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint { 8 | const MIN: Option = None; 9 | const MAX: Option = None; 10 | const EXTENSIBLE: bool = false; 11 | } 12 | 13 | #[derive(Default)] 14 | pub struct NoConstraint; 15 | impl super::common::Constraint for NoConstraint { 16 | const TAG: Tag = Tag::DEFAULT_PRINTABLE_STRING; 17 | } 18 | impl Constraint for NoConstraint {} 19 | 20 | impl WritableType for PrintableString { 21 | type Type = String; 22 | 23 | #[inline] 24 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 25 | writer.write_printable_string::(value.as_str()) 26 | } 27 | } 28 | 29 | impl ReadableType for PrintableString { 30 | type Type = String; 31 | 32 | #[inline] 33 | fn read_value(reader: &mut R) -> Result::Error> { 34 | reader.read_printable_string::() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/descriptor/sequenceof.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct SequenceOf(PhantomData, PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint { 8 | const MIN: Option = None; 9 | const MAX: Option = None; 10 | const EXTENSIBLE: bool = false; 11 | } 12 | 13 | #[derive(Default)] 14 | pub struct NoConstraint; 15 | impl super::common::Constraint for NoConstraint { 16 | const TAG: Tag = Tag::DEFAULT_SEQUENCE_OF; 17 | } 18 | impl Constraint for NoConstraint {} 19 | 20 | impl WritableType for SequenceOf { 21 | type Type = Vec; 22 | 23 | #[inline] 24 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 25 | writer.write_sequence_of::(value.as_slice()) 26 | } 27 | } 28 | 29 | impl ReadableType for SequenceOf { 30 | type Type = Vec; 31 | 32 | #[inline] 33 | fn read_value(reader: &mut R) -> Result::Error> { 34 | reader.read_sequence_of::() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/descriptor/sequence.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use core::marker::PhantomData; 3 | 4 | pub struct Sequence(PhantomData); 5 | 6 | pub trait Constraint: super::common::Constraint { 7 | const NAME: &'static str; 8 | const STD_OPTIONAL_FIELDS: u64; 9 | const FIELD_COUNT: u64; 10 | const EXTENDED_AFTER_FIELD: Option; 11 | 12 | fn read_seq(reader: &mut R) -> Result 13 | where 14 | Self: Sized; 15 | 16 | fn write_seq(&self, writer: &mut W) -> Result<(), W::Error>; 17 | } 18 | 19 | impl WritableType for Sequence { 20 | type Type = C; 21 | 22 | #[inline] 23 | fn write_value( 24 | writer: &mut W, 25 | value: &Self::Type, 26 | ) -> Result<(), ::Error> { 27 | writer.write_sequence::(|w| value.write_seq::(w)) 28 | } 29 | } 30 | 31 | impl ReadableType for Sequence 32 | where 33 | C: Sized, 34 | { 35 | type Type = C; 36 | 37 | #[inline] 38 | fn read_value(reader: &mut R) -> Result::Error> { 39 | reader.read_sequence::(C::read_seq) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/descriptor/null.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct NullT(PhantomData); 6 | 7 | pub trait Constraint: super::common::Constraint {} 8 | 9 | #[derive(Default)] 10 | pub struct NoConstraint; 11 | impl super::common::Constraint for NoConstraint { 12 | const TAG: Tag = Tag::DEFAULT_NULL; 13 | } 14 | impl Constraint for NoConstraint {} 15 | 16 | impl WritableType for NullT { 17 | type Type = Null; 18 | 19 | #[inline] 20 | fn write_value( 21 | writer: &mut W, 22 | value: &Self::Type, 23 | ) -> Result<(), ::Error> { 24 | writer.write_null::(value) 25 | } 26 | } 27 | 28 | impl ReadableType for NullT { 29 | type Type = Null; 30 | 31 | #[inline] 32 | fn read_value(reader: &mut R) -> Result::Error> { 33 | reader.read_null::() 34 | } 35 | } 36 | 37 | #[derive(Default, Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] 38 | pub struct Null; 39 | 40 | impl From<()> for Null { 41 | fn from(_value: ()) -> Self { 42 | Null 43 | } 44 | } 45 | 46 | impl From for () { 47 | fn from(_value: Null) -> Self {} 48 | } 49 | -------------------------------------------------------------------------------- /tests/basic_null.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | use test_utils::*; 3 | 4 | asn_to_rust!( 5 | r"BasicNull DEFINITIONS AUTOMATIC TAGS ::= 6 | BEGIN 7 | 8 | MyNull ::= NULL 9 | 10 | NullSeq ::= SEQUENCE { 11 | abc UTF8String, 12 | def NULL, 13 | ghi MyNull 14 | } 15 | 16 | NullChoice ::= Choice { 17 | abc UTF8String, 18 | def NULL, 19 | ghi MyNull 20 | } 21 | 22 | 23 | END" 24 | ); 25 | 26 | #[test] 27 | fn test_sequence() { 28 | // from playground 29 | serialize_and_deserialize_uper( 30 | 8 * 4, 31 | &[0x03, 0x61, 0x62, 0x63], 32 | &NullSeq { 33 | abc: "abc".to_string(), 34 | def: Null, 35 | ghi: MyNull(Null), 36 | }, 37 | ); 38 | } 39 | 40 | #[test] 41 | fn test_choice_abc() { 42 | // from playground 43 | serialize_and_deserialize_uper( 44 | 8 * 4 + 2, 45 | &[0x00, 0xD8, 0x58, 0x98, 0xC0], 46 | &NullChoice::Abc("abc".to_string()), 47 | ); 48 | } 49 | 50 | #[test] 51 | fn test_choice_def() { 52 | // from playground 53 | serialize_and_deserialize_uper(2, &[0x40], &NullChoice::Def(Null)); 54 | } 55 | 56 | #[test] 57 | fn test_choice_ghi() { 58 | // from playground 59 | serialize_and_deserialize_uper(2, &[0x80], &NullChoice::Ghi(MyNull(Null))); 60 | } 61 | -------------------------------------------------------------------------------- /tests/parse_with_components.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"ParseWithComponents DEFINITIONS AUTOMATIC TAGS ::= 9 | BEGIN 10 | 11 | SomeEnum ::= ENUMERATED { 12 | VarA, 13 | VarB, 14 | VarC 15 | } 16 | 17 | BaseSeq ::= SEQUENCE { 18 | abc UTF8String, 19 | def SomeEnum 20 | } 21 | 22 | SeqButOnlyVarB ::= BaseSeq(WITH COMPONENTS { 23 | ..., 24 | def(VarB) 25 | }) 26 | 27 | END" 28 | ); 29 | 30 | #[test] 31 | pub fn does_it_compile() { 32 | let _ = SomeEnum::VarB; 33 | let _ = BaseSeq { 34 | abc: "some-utf8-string".to_string(), 35 | def: SomeEnum::VarC, 36 | }; 37 | let _ = SeqButOnlyVarB(BaseSeq { 38 | abc: "some-utf8-string-again".to_string(), 39 | def: SomeEnum::VarB, 40 | }); 41 | } 42 | 43 | #[test] 44 | pub fn with_components_must_be_transparent() { 45 | let mut writer1 = UperWriter::default(); 46 | let mut writer2 = UperWriter::default(); 47 | 48 | let base_seq = BaseSeq { 49 | abc: "some-utf8-string-again".to_string(), 50 | def: SomeEnum::VarB, 51 | }; 52 | 53 | base_seq.write(&mut writer1).unwrap(); 54 | SeqButOnlyVarB(base_seq).write(&mut writer2).unwrap(); 55 | 56 | assert_eq!(writer1.into_bytes_vec(), writer2.into_bytes_vec()); 57 | } 58 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/range.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Default, Clone, Copy, PartialOrd, PartialEq, Eq)] 2 | pub struct Range(pub T, pub T, pub bool); 3 | 4 | impl Range { 5 | pub const fn inclusive(min: T, max: T) -> Self { 6 | Self(min, max, false) 7 | } 8 | 9 | pub fn with_extensible(self, extensible: bool) -> Self { 10 | let Range(min, max, _) = self; 11 | Range(min, max, extensible) 12 | } 13 | 14 | pub const fn min(&self) -> &T { 15 | &self.0 16 | } 17 | 18 | pub const fn max(&self) -> &T { 19 | &self.1 20 | } 21 | 22 | pub const fn extensible(&self) -> bool { 23 | self.2 24 | } 25 | 26 | pub fn wrap_opt(self) -> Range> { 27 | let Range(min, max, extensible) = self; 28 | Range(Some(min), Some(max), extensible) 29 | } 30 | } 31 | 32 | impl Range> { 33 | pub fn none() -> Self { 34 | Range(None, None, false) 35 | } 36 | 37 | pub fn min_max(&self, min_fn: impl Fn() -> T, max_fn: impl Fn() -> T) -> Option<(T, T)> 38 | where 39 | T: Copy, 40 | { 41 | match (self.0, self.1) { 42 | (Some(min), Some(max)) => Some((min, max)), 43 | (Some(min), None) => Some((min, max_fn())), 44 | (None, Some(max)) => Some((min_fn(), max)), 45 | (None, None) => None, 46 | } 47 | } 48 | } 49 | 50 | impl Range> {} 51 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/bit_string.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::{Asn, Size}; 2 | use crate::model::Model; 3 | use crate::parse::Error; 4 | use crate::parse::Token; 5 | use crate::resolve::{Error as ResolveError, LitOrRef}; 6 | use crate::resolve::{ResolveState, Resolver, TryResolve, Unresolved}; 7 | use std::convert::TryFrom; 8 | use std::fmt::{Debug, Display}; 9 | use std::iter::Peekable; 10 | 11 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 12 | pub struct BitString { 13 | pub size: Size, 14 | pub constants: Vec<(String, u64)>, 15 | } 16 | 17 | impl> TryFrom<&mut Peekable> 18 | for BitString<::SizeType> 19 | { 20 | type Error = Error; 21 | 22 | fn try_from(iter: &mut Peekable) -> Result { 23 | let constants = Model::>::maybe_read_constants( 24 | iter, 25 | Model::>::constant_u64_parser, 26 | )?; 27 | let size = Model::>::maybe_read_size(iter)?; 28 | Ok(Self { size, constants }) 29 | } 30 | } 31 | 32 | impl TryResolve> for BitString> { 33 | fn try_resolve( 34 | &self, 35 | resolver: &impl Resolver, 36 | ) -> Result, ResolveError> { 37 | Ok(BitString { 38 | size: self.size.try_resolve(resolver)?, 39 | constants: self.constants.clone(), 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/protobuf_optionals.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufOptionals ::= SEQUENCE { 10 | optional-bool BOOLEAN OPTIONAL, 11 | optional-utf8string UTF8String OPTIONAL, 12 | optional-sint32 INTEGER (-2147483648..2147483647) OPTIONAL 13 | } 14 | 15 | END" 16 | ); 17 | 18 | #[test] 19 | #[cfg(feature = "protobuf")] 20 | fn test_optionals_present() { 21 | serialize_and_deserialize_protobuf( 22 | &[8, 1, 18, 6, 115, 116, 114, 105, 110, 103, 24, 84], 23 | &ProtobufOptionals { 24 | optional_bool: Some(true), 25 | optional_utf8string: Some("string".into()), 26 | optional_sint32: Some(42), 27 | }, 28 | ) 29 | } 30 | 31 | #[test] 32 | #[cfg(feature = "protobuf")] 33 | fn test_optionals_absent() { 34 | serialize_and_deserialize_protobuf( 35 | &[], 36 | &ProtobufOptionals { 37 | optional_bool: None, 38 | optional_utf8string: None, 39 | optional_sint32: None, 40 | }, 41 | ) 42 | } 43 | 44 | #[test] 45 | #[cfg(feature = "protobuf")] 46 | fn test_optionals_mixed() { 47 | serialize_and_deserialize_protobuf( 48 | &[8, 0, 24, 242, 20], 49 | &ProtobufOptionals { 50 | optional_bool: Some(false), 51 | optional_utf8string: None, 52 | optional_sint32: Some(1337_i32), 53 | }, 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /tests/resolve_enumerated.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"BasicEnumerated DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | Basic ::= ENUMERATED { 10 | abc, 11 | def, 12 | ghi 13 | } 14 | 15 | Container ::= SEQUENCE { 16 | the-selection Basic DEFAULT abc 17 | } 18 | 19 | 20 | END" 21 | ); 22 | #[test] 23 | pub fn does_it_compile() { 24 | let _ = Basic::Abc; 25 | let _ = Basic::Def; 26 | let _ = Basic::Ghi; 27 | let seq = Container { 28 | the_selection: Basic::Def, 29 | }; 30 | 31 | PrintlnWriter::default().write(&seq).unwrap(); 32 | // Writing sequence MyCleverSeq, tag=Universal(16) 33 | // Writing DEFAULT (default: 1337) 34 | // Some 35 | // WRITING Integer(MIN..MAX), tag=ContextSpecific(0) 36 | // 5 37 | } 38 | 39 | #[test] 40 | pub fn the_selection_abc() { 41 | serialize_and_deserialize_uper( 42 | 1, 43 | &[0x00], 44 | &Container { 45 | the_selection: Basic::Abc, 46 | }, 47 | ); 48 | } 49 | 50 | #[test] 51 | pub fn the_selection_def() { 52 | serialize_and_deserialize_uper( 53 | 3, 54 | &[0xA0], 55 | &Container { 56 | the_selection: Basic::Def, 57 | }, 58 | ); 59 | } 60 | 61 | #[test] 62 | pub fn the_selection_ghi() { 63 | serialize_and_deserialize_uper( 64 | 3, 65 | &[0xC0], 66 | &Container { 67 | the_selection: Basic::Ghi, 68 | }, 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /src/internal_macros.rs: -------------------------------------------------------------------------------- 1 | /// Allows const expansion until `` 2 | /// Cannot be a function with generic type because of `` 3 | macro_rules! const_unwrap_or { 4 | ($op:path, $def:expr) => {{ 5 | // not yet stable clippy lint 6 | #[allow(clippy::manual_unwrap_or)] 7 | match $op { 8 | Some(value) => value, 9 | None => $def, 10 | } 11 | }}; 12 | } 13 | 14 | /// Allows const expansion until `` 15 | /// Cannot be a function with generic type because of `` 16 | macro_rules! const_is_none { 17 | ($op:path) => { 18 | match &$op { 19 | Some(_) => false, 20 | None => true, 21 | } 22 | }; 23 | } 24 | 25 | /// Allows const expansion until `` 26 | /// Cannot be a function with generic type because of `` 27 | macro_rules! const_is_some { 28 | ($op:path) => { 29 | match &$op { 30 | Some(_) => true, 31 | None => false, 32 | } 33 | }; 34 | } 35 | 36 | /// Allows const expansion until `` 37 | /// Cannot be a function with generic type because of `` 38 | macro_rules! const_map_or { 39 | ($op:expr, $fn:expr, $def:expr) => { 40 | match &$op { 41 | Some(v) => $fn(v), 42 | None => $def, 43 | } 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "asn1rs" 3 | version = "0.4.0" 4 | authors = ["Michael Watzko "] 5 | edition = "2021" 6 | description = "ASN.1 to Rust compiler with Protobuf code generator. Supports ASN.1 UPER" 7 | keywords = ["asn1", "uper", "protobuf", "compiler"] 8 | categories = ["encoding", "parsing"] 9 | repository = "https://github.com/kellerkindt/asn1rs" 10 | license = "MIT/Apache-2.0" 11 | readme = "README.md" 12 | 13 | [workspace] 14 | members = [ 15 | "asn1rs-macros", 16 | "asn1rs-model" 17 | ] 18 | 19 | [lib] 20 | name = "asn1rs" 21 | path = "src/lib.rs" 22 | 23 | [[bin]] 24 | name = "asn1rs" 25 | path = "src/main.rs" 26 | required-features = ["model", "clap"] 27 | 28 | 29 | [dependencies] 30 | backtrace = "0.3.69" 31 | 32 | # feature asn1rs-* 33 | asn1rs-model = { version = "0.4.0", path = "asn1rs-model", optional = true } 34 | asn1rs-macros = { version = "0.4.0", path = "asn1rs-macros", optional = true } 35 | 36 | # feature protobuf 37 | byteorder = { version = "1.5.0", optional = true } 38 | 39 | # for binary only 40 | clap = { version = "4.4.18", features = ["derive", "env"], optional = true } 41 | 42 | [dev-dependencies] 43 | syn = { version = "2.0.48", features = ["visit"] } 44 | quote = "1.0.3" 45 | proc-macro2 = "1.0.10" 46 | codegen = "0.2.0" 47 | 48 | [features] 49 | default = ["macros", "model", "clap"] 50 | protobuf = ["asn1rs-model/protobuf", "byteorder"] 51 | macros = ["asn1rs-macros"] 52 | model = ["asn1rs-model"] 53 | debug-proc-macro = ["asn1rs-macros/debug-proc-macro", "asn1rs-model/debug-proc-macro"] 54 | descriptive-deserialize-errors = [] 55 | 56 | [package.metadata.docs.rs] 57 | all-features = true 58 | -------------------------------------------------------------------------------- /tests/basic_sequence.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"BasicSet DEFINITIONS AUTOMATIC TAGS ::= 9 | BEGIN 10 | 11 | Basic ::= [5] SEQUENCE { 12 | abc [APPLICATION 7] UTF8String, 13 | def INTEGER 14 | } 15 | 16 | Extensible ::= [5] SEQUENCE { 17 | abc [APPLICATION 7] UTF8String, 18 | def INTEGER, 19 | ..., 20 | ghi [APPLICATION 2] UTF8String 21 | } 22 | 23 | SomeVal ::= INTEGER (-32768..32767) 24 | 25 | END" 26 | ); 27 | 28 | #[test] 29 | fn does_it_compile() { 30 | let _ = SomeVal(13i16); 31 | } 32 | 33 | #[test] 34 | fn test_basic() { 35 | // from playground 36 | serialize_and_deserialize_uper( 37 | 8 * 15, 38 | &[ 39 | 0x0B, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x02, 0x03, 40 | 0x0A, 41 | ], 42 | &Basic { 43 | abc: "hello world".to_string(), 44 | def: 778, 45 | }, 46 | ); 47 | } 48 | 49 | #[test] 50 | fn test_extensible() { 51 | // from playground 52 | serialize_and_deserialize_uper( 53 | 8 * 29 + 1, 54 | &[ 55 | 0x83, 0xB1, 0x3C, 0xB2, 0x90, 0x31, 0x3C, 0xB2, 0x81, 0x01, 0x83, 0x00, 0x88, 0x07, 56 | 0xB3, 0xB9, 0x32, 0xB0, 0xBA, 0x10, 0x32, 0xBC, 0x3A, 0x32, 0xB7, 0x39, 0xB4, 0xB7, 57 | 0xB7, 0x00, 58 | ], 59 | &Extensible { 60 | abc: "bye bye".to_string(), 61 | def: 774, 62 | ghi: Some("great extension".to_string()), 63 | }, 64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /tests/der_basic_boolean.rs: -------------------------------------------------------------------------------- 1 | use asn1rs::descriptor::boolean::NoConstraint; 2 | use asn1rs::descriptor::{Boolean, ReadableType, WritableType}; 3 | use asn1rs::prelude::basic::DER; 4 | 5 | #[test] 6 | pub fn test_der_basic_boolean() { 7 | for bool_value in [true, false] { 8 | let mut buffer = Vec::new(); 9 | let mut writer = DER::writer(&mut buffer); 10 | 11 | Boolean::::write_value(&mut writer, &bool_value).unwrap(); 12 | 13 | assert_eq!( 14 | &[0x01, 0x01, if bool_value { 0x01 } else { 0x00 }], 15 | &buffer[..] 16 | ); 17 | 18 | let mut reader = DER::reader(&buffer[..]); 19 | let result = Boolean::::read_value(&mut reader).unwrap(); 20 | 21 | assert_eq!(bool_value, result) 22 | } 23 | } 24 | 25 | #[test] 26 | pub fn test_der_basic_boolean_false_from_0x00() { 27 | let mut reader = DER::reader(&[0x01, 0x01, 0xFF][..]); 28 | let result = Boolean::::read_value(&mut reader).unwrap(); 29 | 30 | assert_eq!(true, result) 31 | } 32 | 33 | #[test] 34 | pub fn test_der_basic_boolean_true_from_0xff() { 35 | let mut reader = DER::reader(&[0x01, 0x01, 0xFF][..]); 36 | let result = Boolean::::read_value(&mut reader).unwrap(); 37 | 38 | assert_eq!(true, result) 39 | } 40 | 41 | #[test] 42 | pub fn test_der_basic_boolean_true_from_any_greater_zero() { 43 | for value in 1..=u8::MAX { 44 | let values = [0x01, 0x01, value]; 45 | let mut reader = DER::reader(&values[..]); 46 | let result = Boolean::::read_value(&mut reader).unwrap(); 47 | 48 | assert_eq!(true, result) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/der_basic_number.rs: -------------------------------------------------------------------------------- 1 | use asn1rs::descriptor::numbers::NoConstraint; 2 | use asn1rs::descriptor::numbers::Number; 3 | use asn1rs::descriptor::{Integer, ReadableType, WritableType}; 4 | use asn1rs::prelude::basic::DER; 5 | use std::fmt::Debug; 6 | 7 | fn write_read_integer(len: T) { 8 | let mut buffer = Vec::new(); 9 | let mut writer = DER::writer(&mut buffer); 10 | 11 | Integer::::write_value(&mut writer, &len).unwrap(); 12 | 13 | let mut reader = DER::reader(&buffer[..]); 14 | let read = Integer::::read_value(&mut reader).unwrap(); 15 | 16 | assert_eq!(len, read); 17 | } 18 | 19 | #[test] 20 | pub fn test_length_bounds() { 21 | write_read_integer(0); 22 | write_read_integer(u8::MAX as u64 - 1); 23 | write_read_integer(u8::MAX as u64); 24 | write_read_integer(u8::MAX as u64 + 1); 25 | write_read_integer(u16::MAX as u64 - 1); 26 | write_read_integer(u16::MAX as u64); 27 | write_read_integer(u16::MAX as u64 + 1); 28 | write_read_integer(u32::MAX as u64 - 1); 29 | write_read_integer(u32::MAX as u64); 30 | write_read_integer(u32::MAX as u64 + 1); 31 | write_read_integer(u64::MAX - 1); 32 | write_read_integer(u64::MAX); 33 | } 34 | 35 | #[inline] 36 | pub fn test_letsencrypt_point_numbers() { 37 | const BYTES: &'static [u8] = &[0x80, 0x01, 0x09, 0x81, 0x01, 0x09]; 38 | 39 | let mut reader = DER::reader(BYTES); 40 | 41 | assert_eq!( 42 | 9, 43 | Integer::::read_value(&mut reader).unwrap() 44 | ); 45 | 46 | assert_eq!( 47 | 9, 48 | Integer::::read_value(&mut reader).unwrap() 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /tests/basic_integer.rs: -------------------------------------------------------------------------------- 1 | use asn1rs::prelude::*; 2 | 3 | asn_to_rust!( 4 | r"BasicInteger DEFINITIONS AUTOMATIC TAGS ::= 5 | BEGIN 6 | 7 | RangedMax ::= Integer (0..MAX) 8 | 9 | NotRanged ::= Integer 10 | 11 | END" 12 | ); 13 | 14 | #[test] 15 | fn test_whether_it_compiles_at_all() {} 16 | 17 | #[test] 18 | fn test_default_range() { 19 | assert_eq!(RangedMax::value_min(), NotRanged::value_min()); 20 | assert_eq!(RangedMax::value_max(), NotRanged::value_max()); 21 | let _ = NotRanged(123_u64); // does not compile if the inner type differs 22 | } 23 | 24 | #[test] 25 | fn test_readme_sample() { 26 | use asn1rs::descriptor::numbers::Constraint; 27 | assert_eq!( 28 | ___asn1rs_RangedMaxField0Constraint::MIN, 29 | ___asn1rs_NotRangedField0Constraint::MIN, 30 | ); 31 | assert_eq!( 32 | ___asn1rs_RangedMaxField0Constraint::MAX, 33 | ___asn1rs_NotRangedField0Constraint::MAX, 34 | ); 35 | 36 | let value = NotRanged(123_u64); // does not compile if the inner type is not u64 37 | 38 | let mut writer = UperWriter::default(); 39 | writer.write(&value).expect("Failed to serialize"); 40 | 41 | let mut reader = writer.as_reader(); 42 | let value2 = reader.read::().expect("Failed to deserialize"); 43 | 44 | assert_eq!(value, value2); 45 | } 46 | 47 | #[test] 48 | fn test_uper_small() { 49 | let mut writer = UperWriter::default(); 50 | writer.write(&RangedMax(123)).unwrap(); 51 | assert_eq!(&[0x01, 123_u8], writer.byte_content()); 52 | } 53 | 54 | #[test] 55 | fn test_uper_big() { 56 | let mut writer = UperWriter::default(); 57 | writer.write(&RangedMax(66_000)).unwrap(); 58 | let bytes = 66_000_u64.to_be_bytes(); 59 | 60 | assert_eq!(&[0x03, bytes[5], bytes[6], bytes[7]], writer.byte_content()); 61 | } 62 | -------------------------------------------------------------------------------- /tests/default_boolean.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r#"DefaultString DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | theValue BOOLEAN ::= FALSE 10 | 11 | MyCleverSeq ::= SEQUENCE { 12 | secret-flag BOOLEAN DEFAULT TRUE, 13 | flag-secret BOOLEAN DEFAULT theValue 14 | } 15 | 16 | END"# 17 | ); 18 | 19 | #[test] 20 | pub fn does_it_compile() { 21 | let seq = MyCleverSeq { 22 | secret_flag: true, 23 | flag_secret: true, 24 | }; 25 | let mut writer = PrintlnWriter::default(); 26 | 27 | writer.write(&seq).unwrap(); 28 | // Writing sequence MyCleverSeq, tag=Universal(16) 29 | // Writing DEFAULT (default: true) 30 | // None 31 | // Writing DEFAULT (default: false) 32 | // Some 33 | // WRITING Boolean, tag=Universal(1) 34 | // true 35 | } 36 | 37 | #[test] 38 | pub fn test_seq_with_non_default_value_00() { 39 | serialize_and_deserialize_uper( 40 | 8 * 0 + 3, 41 | &[0x80], 42 | &MyCleverSeq { 43 | secret_flag: false, 44 | flag_secret: false, 45 | }, 46 | ); 47 | } 48 | #[test] 49 | pub fn test_seq_with_non_default_value_01() { 50 | serialize_and_deserialize_uper( 51 | 8 * 0 + 4, 52 | &[0xD0], 53 | &MyCleverSeq { 54 | secret_flag: false, 55 | flag_secret: true, 56 | }, 57 | ); 58 | } 59 | 60 | #[test] 61 | pub fn test_seq_with_default_value_10() { 62 | serialize_and_deserialize_uper( 63 | 8 * 0 + 2, 64 | &[0x00], 65 | &MyCleverSeq { 66 | secret_flag: true, 67 | flag_secret: false, 68 | }, 69 | ); 70 | } 71 | 72 | #[test] 73 | pub fn test_seq_with_non_default_value_11() { 74 | serialize_and_deserialize_uper( 75 | 8 * 0 + 3, 76 | &[0x60], 77 | &MyCleverSeq { 78 | secret_flag: true, 79 | flag_secret: true, 80 | }, 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /asn1rs-model/src/proc_macro/tag.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::Tag; 2 | use proc_macro2::Delimiter; 3 | use syn::parse::{Parse, ParseBuffer}; 4 | 5 | pub struct AttrTag(pub Tag); 6 | 7 | impl Parse for AttrTag { 8 | fn parse<'a>(input: &'a ParseBuffer<'a>) -> syn::Result { 9 | input.step(|s| { 10 | let (group, _span, outer) = s 11 | .group(Delimiter::Parenthesis) 12 | .ok_or_else(|| input.error("Expected parenthesis"))?; 13 | if let Some((variant, cursor)) = group.ident() { 14 | let (variant_group, _span, _outer) = cursor 15 | .group(Delimiter::Parenthesis) 16 | .ok_or_else(|| syn::Error::new(cursor.span(), "Expected parenthesis"))?; 17 | let (number, _cursor) = variant_group.literal().ok_or_else(|| { 18 | syn::Error::new(variant_group.span(), "Expected number literal") 19 | })?; 20 | let number = number.to_string().parse::().map_err(|_| { 21 | syn::Error::new(variant_group.span(), "Literal is not a number") 22 | })?; 23 | Ok(( 24 | AttrTag(match variant.to_string().to_lowercase().as_str() { 25 | "universal" => Tag::Universal(number), 26 | "application" => Tag::Application(number), 27 | "private" => Tag::Private(number), 28 | v => return Err(input.error(format!("Unexpected tag variant `{}`", v))), 29 | }), 30 | outer, 31 | )) 32 | } else if let Some((literal, _cursor)) = group.literal() { 33 | let number = literal 34 | .to_string() 35 | .parse::() 36 | .map_err(|_| syn::Error::new(group.span(), "Literal is not a number"))?; 37 | Ok((AttrTag(Tag::ContextSpecific(number)), outer)) 38 | } else { 39 | Err(syn::Error::new(group.span(), "Expected tag variant")) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/default_string.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r#"DefaultString DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | MyCleverSeq ::= SEQUENCE { 10 | secret-message UTF8String DEFAULT "hey hee ha" 11 | } 12 | 13 | defaultMessage UTF8String ::= "hey hee ha" 14 | 15 | MyCleverSeqRef ::= SEQUENCE { 16 | secret-message UTF8String DEFAULT defaultMessage 17 | } 18 | 19 | END"# 20 | ); 21 | 22 | #[test] 23 | pub fn does_it_compile() { 24 | let seq = MyCleverSeq { 25 | secret_message: "woah".to_string(), 26 | }; 27 | let mut writer = PrintlnWriter::default(); 28 | 29 | writer.write(&seq).unwrap(); 30 | // Writing sequence MyCleverSeq, tag=Universal(16) 31 | // Writing DEFAULT (default: "hey hee ha") 32 | // Some 33 | // Writing Utf8String(MIN..MAX), tag=ContextSpecific(0) 34 | // "woah" 35 | } 36 | 37 | #[test] 38 | pub fn test_seq_with_non_default_value() { 39 | serialize_and_deserialize_uper( 40 | 8 * 10 + 1, 41 | &[ 42 | 0x84, 0xBB, 0xB7, 0xB0, 0xB4, 0x10, 0x3C, 0xB2, 0xB0, 0xB4, 0x00, 43 | ], 44 | &MyCleverSeq { 45 | secret_message: "woah yeah".to_string(), 46 | }, 47 | ); 48 | } 49 | 50 | #[test] 51 | pub fn test_seq_with_default_value() { 52 | serialize_and_deserialize_uper( 53 | 8 * 0 + 1, 54 | &[0x00], 55 | &MyCleverSeq { 56 | secret_message: "hey hee ha".to_string(), 57 | }, 58 | ); 59 | } 60 | 61 | #[test] 62 | pub fn test_ref_with_non_default_value() { 63 | serialize_and_deserialize_uper( 64 | 8 * 10 + 1, 65 | &[ 66 | 0x84, 0xBB, 0xB7, 0xB0, 0xB4, 0x10, 0x3C, 0xB2, 0xB0, 0xB4, 0x00, 67 | ], 68 | &MyCleverSeqRef { 69 | secret_message: "woah yeah".to_string(), 70 | }, 71 | ); 72 | } 73 | 74 | #[test] 75 | pub fn test_ref_with_default_value() { 76 | serialize_and_deserialize_uper( 77 | 8 * 0 + 1, 78 | &[0x00], 79 | &MyCleverSeqRef { 80 | secret_message: "hey hee ha".to_string(), 81 | }, 82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /asn1rs-macros/src/derive_protobuf_eq.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::ToTokens; 3 | use syn::{Data, DataEnum, DataStruct, DataUnion, DeriveInput, Index}; 4 | 5 | pub fn expand(input: DeriveInput) -> TokenStream { 6 | let name = input.ident; 7 | let inner = match input.data { 8 | Data::Struct(data) => expand_struct(data).to_token_stream(), 9 | Data::Enum(data) => expand_enum(data).to_token_stream(), 10 | Data::Union(data) => expand_union(data).to_token_stream(), 11 | }; 12 | TokenStream::from(quote::quote! { 13 | impl ::asn1rs::prelude::ProtobufEq for #name { 14 | fn protobuf_eq(&self, other: &Self) -> bool { 15 | #inner 16 | } 17 | } 18 | }) 19 | } 20 | 21 | fn expand_struct(data: DataStruct) -> impl ToTokens { 22 | let fields = data.fields.iter().enumerate().map(|(index, field)| { 23 | field 24 | .ident 25 | .as_ref() 26 | .map(|i| i.to_token_stream()) 27 | .unwrap_or_else(|| Index::from(index).to_token_stream()) 28 | }); 29 | quote::quote! { 30 | #(::asn1rs::prelude::ProtobufEq::protobuf_eq(&self.#fields, &other.#fields) &&)* true 31 | } 32 | } 33 | 34 | fn expand_enum(data: DataEnum) -> impl ToTokens { 35 | let data_enum = data.variants.iter().any(|d| !d.fields.is_empty()); 36 | let rows = data.variants.iter().map(|variant| &variant.ident); 37 | 38 | if data_enum { 39 | quote::quote! { 40 | match &self { 41 | #( 42 | Self::#rows(me) => if let Self::#rows(other) = &other { 43 | ::asn1rs::prelude::ProtobufEq::protobuf_eq(me, other) 44 | } else { 45 | false 46 | } 47 | ),* 48 | } 49 | } 50 | } else { 51 | quote::quote! { 52 | match &self { 53 | #( 54 | Self::#rows => matches!(other, Self::#rows), 55 | )* 56 | _ => false, 57 | } 58 | } 59 | } 60 | } 61 | 62 | fn expand_union(_data: DataUnion) -> impl ToTokens { 63 | quote::quote! { unimplemented!() } 64 | } 65 | -------------------------------------------------------------------------------- /asn1rs-model/src/proc_macro/size.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::Size; 2 | use syn::parse::{Parse, ParseStream}; 3 | use syn::Ident; 4 | use syn::Lit; 5 | use syn::Token; 6 | 7 | impl Parse for Size { 8 | fn parse<'a>(input: ParseStream) -> syn::Result { 9 | let min = value(input)?.ok_or_else(|| input.error("invalid min"))?; 10 | if input.is_empty() { 11 | Ok(Size::Fix(min, false)) 12 | } else if input.peek(Token![,]) { 13 | let _ = input.parse::()?; 14 | let _ = input.parse::()?; 15 | let _ = input.parse::()?; 16 | let _ = input.parse::()?; 17 | Ok(Size::Fix(min, true)) 18 | } else { 19 | let _ = input.parse::()?; 20 | let _ = input.parse::()?; 21 | let max = value(input)?.ok_or_else(|| input.error("invalid max"))?; 22 | let extensible = if input.peek(Token![,]) && input.peek2(Token![.]) { 23 | let _ = input.parse::()?; 24 | let _ = input.parse::()?; 25 | let _ = input.parse::()?; 26 | let _ = input.parse::()?; 27 | true 28 | } else { 29 | false 30 | }; 31 | 32 | if min == max { 33 | Ok(Size::Fix(min, extensible)) 34 | } else { 35 | Ok(Size::Range(min, max, extensible)) 36 | } 37 | } 38 | } 39 | } 40 | 41 | fn value(input: ParseStream) -> syn::Result> { 42 | if let Ok(Lit::Int(int)) = input.parse::() { 43 | Ok(Some(int.base10_digits().parse::().map_err( 44 | |_| input.error("Expected non-negative int literal"), 45 | )?)) 46 | } else if let Ok(ident) = input.parse::() { 47 | let lc = ident.to_string().to_lowercase(); 48 | if lc == "min" || lc == "max" { 49 | Ok(None) 50 | } else { 51 | Err(input.error(format!( 52 | "Invalid identifier, accepted identifiers are: min, max but got: {}", 53 | lc 54 | ))) 55 | } 56 | } else { 57 | Err(input.error("Cannot parse token")) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/protobuf_enumerated.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufEnum ::= SEQUENCE { 10 | some-enum ENUMERATED { 11 | A, 12 | B, 13 | C 14 | } 15 | } 16 | 17 | ProtobufEnumExt ::= SEQUENCE { 18 | lone-bool BOOLEAN, 19 | some-enum ENUMERATED { 20 | A, 21 | B, 22 | C 23 | }, 24 | lone-int INTEGER 25 | } 26 | 27 | ProtobufOuterEnum ::= ENUMERATED { 28 | A, 29 | B, 30 | C 31 | } 32 | 33 | END" 34 | ); 35 | 36 | #[test] 37 | #[cfg(feature = "protobuf")] 38 | fn test_enumeration_a() { 39 | serialize_and_deserialize_protobuf( 40 | &[8, 0], 41 | &ProtobufEnum { 42 | some_enum: ProtobufEnumSomeEnum::A, 43 | }, 44 | ) 45 | } 46 | 47 | #[test] 48 | #[cfg(feature = "protobuf")] 49 | fn test_enumeration_b() { 50 | serialize_and_deserialize_protobuf( 51 | &[8, 1], 52 | &ProtobufEnum { 53 | some_enum: ProtobufEnumSomeEnum::B, 54 | }, 55 | ) 56 | } 57 | 58 | #[test] 59 | #[cfg(feature = "protobuf")] 60 | fn test_enumeration_c() { 61 | serialize_and_deserialize_protobuf( 62 | &[8, 2], 63 | &ProtobufEnum { 64 | some_enum: ProtobufEnumSomeEnum::C, 65 | }, 66 | ) 67 | } 68 | 69 | #[test] 70 | #[cfg(feature = "protobuf")] 71 | fn test_enumeration_ext_b() { 72 | serialize_and_deserialize_protobuf( 73 | &[8, 0, 16, 1, 24, 217, 2], 74 | &ProtobufEnumExt { 75 | lone_bool: false, 76 | some_enum: ProtobufEnumExtSomeEnum::B, 77 | lone_int: 345_u64, 78 | }, 79 | ) 80 | } 81 | 82 | #[test] 83 | #[cfg(feature = "protobuf")] 84 | fn test_enumeration_outer_a() { 85 | serialize_and_deserialize_protobuf(&[0], &ProtobufOuterEnum::A) 86 | } 87 | 88 | #[test] 89 | #[cfg(feature = "protobuf")] 90 | fn test_enumeration_outer_b() { 91 | serialize_and_deserialize_protobuf(&[1], &ProtobufOuterEnum::B) 92 | } 93 | 94 | #[test] 95 | #[cfg(feature = "protobuf")] 96 | fn test_enumeration_outer_c() { 97 | serialize_and_deserialize_protobuf(&[2], &ProtobufOuterEnum::C) 98 | } 99 | -------------------------------------------------------------------------------- /tests/default_integer.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r#"DefaultInteger DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | MyCleverSeq ::= SEQUENCE { 10 | secret-code INTEGER DEFAULT 1337 11 | } 12 | 13 | theRefValue INTEGER(-9999..9999) ::= -1337 14 | 15 | MyCleverSeqRef ::= SEQUENCE { 16 | secret-code INTEGER(-9999..9999) DEFAULT theRefValue 17 | } 18 | 19 | MyWrappedInteger ::= Integer { 20 | some-unit(1) 21 | } 22 | 23 | MyCleverSeqWrapped ::= SEQUENCE { 24 | secret-code MyWrappedInteger DEFAULT 1337 25 | } 26 | 27 | END"# 28 | ); 29 | 30 | #[test] 31 | pub fn does_it_compile() { 32 | let seq = MyCleverSeq { secret_code: 5 }; 33 | let mut writer = PrintlnWriter::default(); 34 | 35 | writer.write(&seq).unwrap(); 36 | // Writing sequence MyCleverSeq, tag=Universal(16) 37 | // Writing DEFAULT (default: 1337) 38 | // Some 39 | // WRITING Integer(MIN..MAX), tag=ContextSpecific(0) 40 | // 5 41 | 42 | let _ = MyCleverSeqWrapped { 43 | secret_code: MyWrappedInteger(1337), 44 | }; 45 | } 46 | 47 | #[test] 48 | pub fn test_seq_with_non_default_value_0() { 49 | serialize_and_deserialize_uper( 50 | 8 * 2 + 1, 51 | &[0x80, 0x80, 0x00], 52 | &MyCleverSeq { secret_code: 0 }, 53 | ); 54 | } 55 | 56 | #[test] 57 | pub fn test_seq_with_non_default_value_1500() { 58 | serialize_and_deserialize_uper( 59 | 8 * 3 + 1, 60 | &[0x81, 0x02, 0xEE, 0x00], 61 | &MyCleverSeq { secret_code: 1500 }, 62 | ); 63 | } 64 | 65 | #[test] 66 | pub fn test_seq_with_default_value() { 67 | serialize_and_deserialize_uper(8 * 0 + 1, &[0x00], &MyCleverSeq { secret_code: 1337 }); 68 | } 69 | 70 | #[test] 71 | pub fn test_ref_with_non_default_value_0() { 72 | serialize_and_deserialize_uper(8 * 2 + 0, &[0xA7, 0x0F], &MyCleverSeqRef { secret_code: 0 }); 73 | } 74 | 75 | #[test] 76 | pub fn test_ref_with_non_default_value_1500() { 77 | serialize_and_deserialize_uper( 78 | 8 * 2 + 0, 79 | &[0xAC, 0xEB], 80 | &MyCleverSeqRef { secret_code: 1500 }, 81 | ); 82 | } 83 | 84 | #[test] 85 | pub fn test_ref_with_default_value() { 86 | serialize_and_deserialize_uper(8 * 0 + 1, &[0x00], &MyCleverSeqRef { secret_code: -1337 }); 87 | } 88 | -------------------------------------------------------------------------------- /tests/basic_sequence_of.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"BasicSequenceOf DEFINITIONS AUTOMATIC TAGS ::= 9 | BEGIN 10 | 11 | Unconstrained ::= SEQUENCE OF INTEGER 12 | 13 | BasicConstrained ::= SEQUENCE SIZE(3) OF INTEGER 14 | 15 | BasicConstrainedSmall ::= SEQUENCE (SIZE(2..3)) OF INTEGER 16 | 17 | BasicConstrainedExtensible ::= SEQUENCE SIZE(2..3,...) OF INTEGER 18 | 19 | END" 20 | ); 21 | 22 | #[test] 23 | fn test_unconstrained() { 24 | // from playground 25 | serialize_and_deserialize_uper( 26 | 8 * 11, 27 | &[ 28 | 0x05, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 29 | ], 30 | &Unconstrained(vec![1, 2, 3, 4, 5]), 31 | ); 32 | } 33 | 34 | #[test] 35 | fn test_fixed_size() { 36 | // from playground 37 | serialize_and_deserialize_uper( 38 | 8 * 6, 39 | &[0x01, 0x01, 0x01, 0x02, 0x01, 0x03], 40 | &BasicConstrained(vec![1, 2, 3]), 41 | ); 42 | } 43 | 44 | #[test] 45 | #[should_panic(expected = "SizeNotInRange(5, 2, 3)")] 46 | fn test_too_large() { 47 | // from playground 48 | serialize_and_deserialize_uper(0, &[], &BasicConstrainedSmall(vec![1, 2, 3, 4, 5])); 49 | } 50 | 51 | #[test] 52 | fn test_small_min() { 53 | // from playground 54 | serialize_and_deserialize_uper( 55 | 8 * 4 + 1, 56 | &[0x00, 0x80, 0x80, 0x81, 0x00], 57 | &BasicConstrainedSmall(vec![1, 2]), 58 | ); 59 | } 60 | 61 | #[test] 62 | fn test_small_max() { 63 | // from playground 64 | serialize_and_deserialize_uper( 65 | 8 * 6 + 1, 66 | &[0x80, 0x80, 0x80, 0x81, 0x00, 0x81, 0x80], 67 | &BasicConstrainedSmall(vec![1, 2, 3]), 68 | ); 69 | } 70 | 71 | #[test] 72 | fn test_extensible_small() { 73 | // from playground 74 | serialize_and_deserialize_uper( 75 | 8 * 6 + 2, 76 | &[0x40, 0x40, 0x40, 0x40, 0x80, 0x40, 0xC0], 77 | &BasicConstrainedExtensible(vec![1, 2, 3]), 78 | ); 79 | } 80 | 81 | #[test] 82 | fn test_extensible_extended() { 83 | // from playground 84 | serialize_and_deserialize_uper( 85 | 8 * 11 + 1, 86 | &[ 87 | 0x82, 0x80, 0x80, 0x80, 0x81, 0x00, 0x81, 0x80, 0x82, 0x00, 0x82, 0x80, 88 | ], 89 | &BasicConstrainedExtensible(vec![1, 2, 3, 4, 5]), 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /tests/derive_protobuf_eq.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | #![cfg(feature = "protobuf")] 3 | 4 | use asn1rs::prelude::*; 5 | 6 | #[derive(ProtobufEq)] 7 | pub struct SimpleStruct { 8 | maybe_some_number: Option, 9 | } 10 | 11 | #[test] 12 | pub fn test_none_is_eq_to_zero() { 13 | assert!(SimpleStruct { 14 | maybe_some_number: None, 15 | } 16 | .protobuf_eq(&SimpleStruct { 17 | maybe_some_number: Some(0), 18 | })) 19 | } 20 | 21 | #[test] 22 | pub fn test_none_is_non_eq_to_one() { 23 | assert!(!SimpleStruct { 24 | maybe_some_number: None, 25 | } 26 | .protobuf_eq(&SimpleStruct { 27 | maybe_some_number: Some(1), 28 | })) 29 | } 30 | 31 | #[test] 32 | pub fn test_one_is_eq_to_one() { 33 | assert!(SimpleStruct { 34 | maybe_some_number: Some(1), 35 | } 36 | .protobuf_eq(&SimpleStruct { 37 | maybe_some_number: Some(1), 38 | })) 39 | } 40 | 41 | #[test] 42 | pub fn test_two_is_non_eq_to_one() { 43 | assert!(!SimpleStruct { 44 | maybe_some_number: Some(2), 45 | } 46 | .protobuf_eq(&SimpleStruct { 47 | maybe_some_number: Some(1), 48 | })) 49 | } 50 | 51 | #[derive(ProtobufEq)] 52 | pub struct TupleStruct(Option); 53 | 54 | #[test] 55 | pub fn test_tuple_struct() { 56 | assert!(TupleStruct(None).protobuf_eq(&TupleStruct(Some(0)))); 57 | assert!(TupleStruct(Some(0)).protobuf_eq(&TupleStruct(Some(0)))); 58 | assert!(!TupleStruct(Some(1)).protobuf_eq(&TupleStruct(Some(0)))); 59 | } 60 | 61 | #[derive(ProtobufEq)] 62 | pub enum DataEnum { 63 | Abc(u64), 64 | Def(Option), 65 | Ghi(TupleStruct), 66 | } 67 | 68 | #[test] 69 | pub fn test_data_enum() { 70 | assert!(DataEnum::Def(None).protobuf_eq(&DataEnum::Def(Some(0)))); 71 | assert!(DataEnum::Def(Some(0)).protobuf_eq(&DataEnum::Def(Some(0)))); 72 | assert!(!DataEnum::Def(Some(1)).protobuf_eq(&DataEnum::Def(Some(0)))); 73 | assert!(!DataEnum::Abc(1).protobuf_eq(&DataEnum::Ghi(TupleStruct(None)))); 74 | } 75 | 76 | #[derive(ProtobufEq)] 77 | pub enum SimpleEnum { 78 | Abc, 79 | Def, 80 | Ghi, 81 | } 82 | 83 | #[test] 84 | pub fn test_simple_enum() { 85 | assert!(SimpleEnum::Abc.protobuf_eq(&SimpleEnum::Abc)); 86 | assert!(SimpleEnum::Def.protobuf_eq(&SimpleEnum::Def)); 87 | assert!(SimpleEnum::Ghi.protobuf_eq(&SimpleEnum::Ghi)); 88 | assert!(!SimpleEnum::Abc.protobuf_eq(&SimpleEnum::Ghi)); 89 | } 90 | -------------------------------------------------------------------------------- /tests/der_print_container.rs: -------------------------------------------------------------------------------- 1 | use asn1rs::prelude::basic::BasicRead; 2 | use std::io::Read; 3 | 4 | fn print(bin: &[u8], depth: u16) -> Vec { 5 | let mut result = Vec::default(); 6 | let reader = &mut &*bin; 7 | while !reader.is_empty() { 8 | let identifier = reader.read_identifier().unwrap(); 9 | let len = reader.read_length().unwrap(); 10 | 11 | let mut bin = core::iter::repeat(0u8) 12 | .take(len as usize) 13 | .collect::>(); 14 | 15 | reader.read_exact(&mut bin[..]).unwrap(); 16 | 17 | result.push(format!( 18 | "{} - {identifier:?} {len} {bin:?}", 19 | core::iter::repeat(' ') 20 | .take(usize::from(depth) * 2) 21 | .collect::() 22 | )); 23 | 24 | if identifier.value() as u8 & 0b0010_0000 != 0 { 25 | result.extend(print(&bin, depth + 1)) 26 | } 27 | } 28 | result 29 | } 30 | 31 | fn print_compare(multiline: &str, bin: &[u8]) { 32 | let lines = print(bin, 0); 33 | let lines = lines.iter().map(String::as_str).collect::>(); 34 | lines.iter().for_each(|l| println!("{l}")); 35 | 36 | let lines_given = multiline.trim().lines().collect::>(); 37 | 38 | for i in 0..lines.len().min(lines_given.len()) { 39 | let expected = &lines_given[i]; 40 | let got = &lines[i]; 41 | assert_eq!(expected.trim(), got.trim()); 42 | } 43 | assert_eq!(lines_given.len(), lines.len()); 44 | } 45 | 46 | #[test] 47 | fn print_simple_boolean() { 48 | print_compare( 49 | r#" 50 | - Universal(1) 1 [255] 51 | "#, 52 | &[0x01, 0x01, 0xFF], 53 | ); 54 | } 55 | 56 | #[test] 57 | fn print_letsencrypt_point_x_y() { 58 | print_compare( 59 | r#" 60 | - Universal(48) 6 [128, 1, 9, 129, 1, 9] 61 | - ContextSpecific(0) 1 [9] 62 | - ContextSpecific(1) 1 [9] 63 | "#, 64 | &[0x30, 0x06, 0x80, 0x01, 0x09, 0x81, 0x01, 0x09], 65 | ); 66 | } 67 | 68 | #[test] 69 | fn print_letsencrypt_sequence() { 70 | print_compare( 71 | r#" 72 | - Universal(48) 13 [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0] 73 | - Universal(6) 9 [42, 134, 72, 134, 247, 13, 1, 1, 11] 74 | - Universal(5) 0 [] 75 | "#, 76 | &[ 77 | 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 78 | 0x00, 79 | ], 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /tests/showcase.rs: -------------------------------------------------------------------------------- 1 | use asn1rs::prelude::*; 2 | 3 | asn_to_rust!( 4 | r#"BasicSchema DEFINITIONS AUTOMATIC TAGS ::= 5 | BEGIN 6 | 7 | Pizza ::= SEQUENCE { 8 | price INTEGER, 9 | size INTEGER(1..4), 10 | note UTF8String OPTIONAL 11 | } 12 | 13 | Topping ::= ENUMERATED { 14 | not-pineapple, 15 | even-less-pineapple, 16 | no-pineapple-at-all 17 | } 18 | 19 | Custom ::= UTF8String 20 | 21 | WhatToEat ::= CHOICE { 22 | pizza Pizza, 23 | custom Custom 24 | } 25 | 26 | END"# 27 | ); 28 | 29 | // This module contains the same content which is also generated by the macro call above 30 | mod what_is_being_generated { 31 | use asn1rs::prelude::*; 32 | 33 | #[asn(sequence)] 34 | #[derive(Default, Debug, Clone, PartialEq, Hash)] 35 | pub struct Pizza { 36 | #[asn(integer(min..max))] 37 | pub price: u64, 38 | #[asn(integer(1..4))] 39 | pub size: u8, 40 | #[asn(optional(utf8string))] 41 | pub note: Option, 42 | } 43 | 44 | #[asn(enumerated)] 45 | #[derive(Debug, Clone, PartialEq, Hash, Copy, PartialOrd, Eq)] 46 | pub enum Topping { 47 | NotPineapple, 48 | EvenLessPineapple, 49 | NoPineappleAtAll, 50 | } 51 | 52 | #[asn(transparent)] 53 | #[derive(Default, Debug, Clone, PartialEq, Hash)] 54 | pub struct Custom(#[asn(utf8string)] pub String); 55 | 56 | #[asn(choice)] 57 | #[derive(Debug, Clone, PartialEq, Hash)] 58 | pub enum WhatToEat { 59 | #[asn(complex(Pizza, tag(UNIVERSAL(16))))] 60 | Pizza(Pizza), 61 | #[asn(complex(Custom, tag(UNIVERSAL(16))))] 62 | Custom(Custom), 63 | } 64 | } 65 | 66 | #[test] 67 | fn uper_proof() { 68 | use asn1rs::prelude::UperWriter; 69 | let mut writer = UperWriter::default(); 70 | writer 71 | .write(&WhatToEat::Pizza(Pizza { 72 | price: 2, 73 | size: 3, 74 | note: Some(String::from("Extra crusty!")), 75 | })) 76 | .unwrap(); 77 | 78 | // read into the plain type to prove they behave the same 79 | use what_is_being_generated as g; 80 | 81 | let mut reader = writer.as_reader(); 82 | let read = reader.read::().expect("Failed to read"); 83 | 84 | assert_eq!( 85 | read, 86 | g::WhatToEat::Pizza(g::Pizza { 87 | price: 2, 88 | size: 3, 89 | note: Some(String::from("Extra crusty!")) 90 | }) 91 | ); 92 | } 93 | -------------------------------------------------------------------------------- /src/protocol/basic/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains defines traits to encode and decode basic ASN.1 primitives and types of 2 | //! the basic family (BER, DER, CER). 3 | 4 | mod distinguished; 5 | mod err; 6 | 7 | pub use distinguished::*; 8 | pub use err::Error; 9 | 10 | use asn1rs_model::asn::Tag; 11 | 12 | /// According to ITU-T X.690 13 | pub trait BasicRead { 14 | type Flavor; 15 | 16 | /// According to ITU-T X.690, chapter 8.1.2, an identifier octet contains the class and number 17 | /// of the type. 18 | fn read_identifier(&mut self) -> Result; 19 | 20 | /// According to ITU-T X.690, chapter 8.1.3, the length is encoded in at least one byte, in 21 | /// either the short (8.1.3.4) or long (8.1.3.5) form 22 | fn read_length(&mut self) -> Result; 23 | 24 | /// According to ITU-T X.690, chapter 8.2, the boolean type is represented in a single byte, 25 | /// where 0 represents `false` and any other value represents `true`. 26 | fn read_boolean(&mut self) -> Result; 27 | 28 | /// According to ITU-T X.690, chapter 8.3, the integer type is represented in a series of bytes. 29 | fn read_integer_i64(&mut self, byte_len: u32) -> Result; 30 | 31 | /// According to ITU-T X.690, chapter 8.3, the integer type is represented in a series of bytes. 32 | fn read_integer_u64(&mut self, byte_len: u32) -> Result; 33 | } 34 | 35 | /// According to ITU-T X.690 36 | pub trait BasicWrite { 37 | type Flavor; 38 | 39 | /// According to ITU-T X.690, chapter 8.1.2, an identifier octet contains the class and number 40 | /// of the type. 41 | fn write_identifier(&mut self, tag: Tag) -> Result<(), Error>; 42 | 43 | /// According to ITU-T X.690, chapter 8.1.3, the length is encoded in at least one byte, in 44 | /// either the short (8.1.3.4) or long (8.1.3.5) form 45 | fn write_length(&mut self, length: u64) -> Result<(), Error>; 46 | 47 | /// According to ITU-T X.690, chapter 8.2, the boolean type is represented in a single byte, 48 | /// where 0 represents `false` and any other value represents `true`. 49 | fn write_boolean(&mut self, value: bool) -> Result<(), Error>; 50 | 51 | /// According to ITU-T X.690, chapter 8.3, the integer type is represented in a series of bytes. 52 | fn write_integer_i64(&mut self, value: i64) -> Result<(), Error>; 53 | 54 | /// According to ITU-T X.690, chapter 8.3, the integer type is represented in a series of bytes. 55 | fn write_integer_u64(&mut self, value: u64) -> Result<(), Error>; 56 | } 57 | -------------------------------------------------------------------------------- /tests/protobuf_set_of.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufSetOf ::= SEQUENCE { 10 | many-sint32 SET OF INTEGER (-2147483648..2147483647) 11 | } 12 | 13 | ProtobufSetOfExt ::= SEQUENCE { 14 | lone-bool BOOLEAN, 15 | many-sint32 SET OF INTEGER (-2147483648..2147483647), 16 | another-string UTF8String 17 | } 18 | 19 | END" 20 | ); 21 | 22 | #[test] 23 | #[cfg(feature = "protobuf")] 24 | fn test_set_of_empty() { 25 | serialize_and_deserialize_protobuf( 26 | &[], 27 | &ProtobufSetOf { 28 | many_sint32: Vec::default(), 29 | }, 30 | ) 31 | } 32 | 33 | #[test] 34 | #[cfg(feature = "protobuf")] 35 | fn test_set_of_empty_ext() { 36 | serialize_and_deserialize_protobuf( 37 | &[8, 1, 26, 5, 101, 109, 112, 116, 121], 38 | &ProtobufSetOfExt { 39 | lone_bool: true, 40 | many_sint32: Vec::default(), 41 | another_string: "empty".into(), 42 | }, 43 | ) 44 | } 45 | 46 | #[test] 47 | #[cfg(feature = "protobuf")] 48 | fn test_set_of_single() { 49 | serialize_and_deserialize_protobuf( 50 | &[8, 1], 51 | &ProtobufSetOf { 52 | many_sint32: vec![-1_i32], 53 | }, 54 | ) 55 | } 56 | 57 | #[test] 58 | #[cfg(feature = "protobuf")] 59 | fn test_set_of_single_ext() { 60 | serialize_and_deserialize_protobuf( 61 | &[8, 0, 16, 1, 26, 6, 115, 105, 110, 103, 108, 101], 62 | &ProtobufSetOfExt { 63 | lone_bool: false, 64 | many_sint32: vec![-1_i32], 65 | another_string: "single".into(), 66 | }, 67 | ) 68 | } 69 | 70 | #[test] 71 | #[cfg(feature = "protobuf")] 72 | fn test_set_of_multiple() { 73 | serialize_and_deserialize_protobuf( 74 | &[8, 1, 8, 4, 8, 6, 8, 8, 8, 128, 16, 8, 255, 143, 226, 9], 75 | &ProtobufSetOf { 76 | many_sint32: vec![-1_i32, 2, 3, 4, 1024, -1024_1024], 77 | }, 78 | ) 79 | } 80 | 81 | #[test] 82 | #[cfg(feature = "protobuf")] 83 | fn test_set_of_multiple_ext() { 84 | serialize_and_deserialize_protobuf( 85 | &[ 86 | 8, 0, 16, 1, 16, 4, 16, 6, 16, 8, 16, 128, 16, 16, 255, 143, 226, 9, 26, 8, 109, 117, 87 | 108, 116, 105, 112, 108, 101, 88 | ], 89 | &ProtobufSetOfExt { 90 | lone_bool: false, 91 | many_sint32: vec![-1_i32, 2, 3, 4, 1024, -1024_1024], 92 | another_string: "multiple".into(), 93 | }, 94 | ) 95 | } 96 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![warn(unused_extern_crates)] 3 | 4 | mod converter; 5 | use converter::Converter; 6 | 7 | pub fn main() { 8 | let params = ::parse(); 9 | let mut converter = Converter::default(); 10 | 11 | for source in ¶ms.source_files { 12 | if let Err(e) = converter.load_file(source) { 13 | println!("Failed to load file {}: {:?}", source, e); 14 | return; 15 | } 16 | } 17 | 18 | let result = match params.conversion_target { 19 | ConversionTarget::Rust => converter.to_rust(¶ms.destination_dir, |rust| { 20 | rust.set_fields_pub(!params.rust_fields_not_public); 21 | rust.set_fields_have_getter_and_setter(params.rust_getter_and_setter); 22 | }), 23 | #[cfg(feature = "protobuf")] 24 | ConversionTarget::Proto => converter.to_protobuf(¶ms.destination_dir), 25 | }; 26 | 27 | match result { 28 | Err(e) => println!("Failed to convert: {:?}", e), 29 | Ok(files) => { 30 | for (source, mut files) in files { 31 | println!("Successfully converted {} => {}", source, files.remove(0)); 32 | files 33 | .iter() 34 | .for_each(|f| println!(" => {}", f)); 35 | } 36 | } 37 | } 38 | } 39 | 40 | #[derive(clap::Parser, Debug)] 41 | #[command(author, version, about, long_about = None)] // Read from `Cargo.toml` 42 | pub struct Parameters { 43 | #[arg( 44 | short = 'n', 45 | long = "rust-fields-not-public", 46 | env = "RUST_FIELDS_NOT_PUBLIC", 47 | help = "Whether the fields in the generated rust code are marked 'pub'" 48 | )] 49 | pub rust_fields_not_public: bool, 50 | #[arg( 51 | short = 'g', 52 | long = "rust-getter-and-setter", 53 | env = "RUST_GETTER_AND_SETTER", 54 | help = "Whether to generate getter and setter for the fields of the generated rust structs" 55 | )] 56 | pub rust_getter_and_setter: bool, 57 | #[arg( 58 | value_enum, 59 | short = 't', 60 | long = "convert-to", 61 | env = "CONVERT_TO", 62 | help = "The target to convert the input files to", 63 | default_value = "rust" 64 | )] 65 | pub conversion_target: ConversionTarget, 66 | #[arg(env = "DESTINATION_DIR")] 67 | pub destination_dir: String, 68 | #[arg(env = "SOURCE_FILES")] 69 | pub source_files: Vec, 70 | } 71 | 72 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] 73 | pub enum ConversionTarget { 74 | Rust, 75 | #[cfg(feature = "protobuf")] 76 | Proto, 77 | } 78 | -------------------------------------------------------------------------------- /tests/octet_string.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | MyCleverSeq ::= SEQUENCE { 10 | secret-codes OCTET STRING (SIZE(5..12)) 11 | } 12 | 13 | MyCleverSeq2 ::= SEQUENCE { 14 | secret-codes OCTET STRING (SIZE(1..2)) 15 | } 16 | 17 | MyCleverSeq3 ::= SEQUENCE { 18 | secret-codes OCTET STRING (SIZE(1..2,...)) 19 | } 20 | 21 | MyCleverSeq4 ::= SEQUENCE { 22 | secret-codes OCTET STRING (SIZE(2..2,...)) 23 | } 24 | 25 | MyCleverSeq5 ::= SEQUENCE { 26 | secret-codes OCTET STRING (SIZE(2..2)) 27 | } 28 | 29 | END" 30 | ); 31 | 32 | #[test] 33 | fn test_my_clever_seq_min_max() { 34 | use asn1rs::descriptor::octetstring::Constraint; 35 | assert_eq!( 36 | Some(5), 37 | ___asn1rs_MyCleverSeqFieldSecretCodesConstraint::MIN 38 | ); 39 | assert_eq!( 40 | Some(12), 41 | ___asn1rs_MyCleverSeqFieldSecretCodesConstraint::MAX 42 | ); 43 | } 44 | 45 | #[test] 46 | fn test_octet_string_constraint() { 47 | serialize_and_deserialize_uper( 48 | 43, 49 | &[0x02, 0x46, 0x8A, 0xCF, 0x12, 0x00], 50 | &MyCleverSeq { 51 | secret_codes: vec![0x12, 0x34, 0x56, 0x78, 0x90], 52 | }, 53 | ) 54 | } 55 | 56 | #[test] 57 | fn test_octet_string_very_short() { 58 | serialize_and_deserialize_uper( 59 | 9, 60 | &[0x09, 0x00], 61 | &MyCleverSeq2 { 62 | secret_codes: vec![0x12], 63 | }, 64 | ) 65 | } 66 | 67 | #[test] 68 | fn test_octet_string_extended() { 69 | serialize_and_deserialize_uper( 70 | 41, 71 | &[0x82, 0x09, 0x1A, 0x2B, 0x3C, 0x00], 72 | &MyCleverSeq3 { 73 | secret_codes: vec![0x12, 0x34, 0x56, 0x78], 74 | }, 75 | ) 76 | } 77 | 78 | #[test] 79 | fn test_octet_string_fixed_extended() { 80 | serialize_and_deserialize_uper( 81 | 41, 82 | &[0x82, 0x09, 0x1A, 0x2B, 0x3C, 0x00], 83 | &MyCleverSeq3 { 84 | secret_codes: vec![0x12, 0x34, 0x56, 0x78], 85 | }, 86 | ) 87 | } 88 | 89 | #[test] 90 | fn test_octet_string_fixed() { 91 | serialize_and_deserialize_uper( 92 | 17, 93 | &[0x09, 0x1A, 0x00], 94 | &MyCleverSeq4 { 95 | secret_codes: vec![0x12, 0x34], 96 | }, 97 | ) 98 | } 99 | 100 | #[test] 101 | fn test_octet_string_fixed_unextendable() { 102 | serialize_and_deserialize_uper( 103 | 16, 104 | &[0x12, 0x34], 105 | &MyCleverSeq5 { 106 | secret_codes: vec![0x12, 0x34], 107 | }, 108 | ) 109 | } 110 | -------------------------------------------------------------------------------- /tests/basic_set_of.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"BasicSequenceOf DEFINITIONS AUTOMATIC TAGS ::= 9 | BEGIN 10 | 11 | Unconstrained ::= SET OF INTEGER 12 | 13 | BasicConstrained ::= SET SIZE(3) OF INTEGER 14 | 15 | BasicConstrainedSmall ::= SET (SIZE(2..3)) OF INTEGER 16 | 17 | BasicConstrainedExtensible ::= SET SIZE(2..3,...) OF INTEGER 18 | 19 | END" 20 | ); 21 | 22 | #[test] 23 | fn test_unconstrained() { 24 | // from playground 25 | serialize_and_deserialize_uper( 26 | 8 * 11, 27 | &[ 28 | 0x05, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 29 | ], 30 | &Unconstrained(vec![1, 2, 3, 4, 5]), 31 | ); 32 | } 33 | 34 | #[test] 35 | fn test_unconstrained_rev() { 36 | // from playground 37 | serialize_and_deserialize_uper( 38 | 8 * 11, 39 | &[ 40 | 0x05, 0x01, 0x05, 0x01, 0x04, 0x01, 0x03, 0x01, 0x02, 0x01, 0x01, 41 | ], 42 | &Unconstrained(vec![5, 4, 3, 2, 1]), 43 | ); 44 | } 45 | 46 | #[test] 47 | fn test_fixed_size() { 48 | // from playground 49 | serialize_and_deserialize_uper( 50 | 8 * 6, 51 | &[0x01, 0x01, 0x01, 0x02, 0x01, 0x03], 52 | &BasicConstrained(vec![1, 2, 3]), 53 | ); 54 | } 55 | 56 | #[test] 57 | #[should_panic(expected = "SizeNotInRange(5, 2, 3)")] 58 | fn test_too_large() { 59 | // from playground 60 | serialize_and_deserialize_uper(0, &[], &BasicConstrainedSmall(vec![1, 2, 3, 4, 5])); 61 | } 62 | 63 | #[test] 64 | fn test_small_min() { 65 | // from playground 66 | serialize_and_deserialize_uper( 67 | 8 * 4 + 1, 68 | &[0x00, 0x80, 0x80, 0x81, 0x00], 69 | &BasicConstrainedSmall(vec![1, 2]), 70 | ); 71 | } 72 | 73 | #[test] 74 | fn test_small_max() { 75 | // from playground 76 | serialize_and_deserialize_uper( 77 | 8 * 6 + 1, 78 | &[0x80, 0x80, 0x80, 0x81, 0x00, 0x81, 0x80], 79 | &BasicConstrainedSmall(vec![1, 2, 3]), 80 | ); 81 | } 82 | 83 | #[test] 84 | fn test_extensible_small() { 85 | // from playground 86 | serialize_and_deserialize_uper( 87 | 8 * 6 + 2, 88 | &[0x40, 0x40, 0x40, 0x40, 0x80, 0x40, 0xC0], 89 | &BasicConstrainedExtensible(vec![1, 2, 3]), 90 | ); 91 | } 92 | 93 | #[test] 94 | fn test_extensible_extended() { 95 | // from playground 96 | serialize_and_deserialize_uper( 97 | 8 * 11 + 1, 98 | &[ 99 | 0x82, 0x80, 0x80, 0x80, 0x81, 0x00, 0x81, 0x80, 0x82, 0x80, 0x83, 0x00, 100 | ], 101 | &BasicConstrainedExtensible(vec![1, 2, 3, 5, 6]), 102 | ); 103 | } 104 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/components.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::peekable::PeekableTokens; 2 | use crate::asn::{Asn, Type}; 3 | use crate::model::{Field, Model}; 4 | use crate::parse::Error; 5 | use crate::parse::Token; 6 | use crate::resolve::{Error as ResolveError, Resolved, Resolver}; 7 | use crate::resolve::{ResolveState, Unresolved}; 8 | use std::convert::TryFrom; 9 | use std::iter::Peekable; 10 | 11 | /// ITU-T X.680 | ISO/IEC 8824-1:2015, Annex L 12 | #[derive(Debug, Clone, PartialOrd, PartialEq)] 13 | pub struct ComponentTypeList { 14 | pub fields: Vec>>, 15 | pub extension_after: Option, 16 | } 17 | 18 | impl> TryFrom<&mut Peekable> for ComponentTypeList { 19 | type Error = Error; 20 | 21 | fn try_from(iter: &mut Peekable) -> Result { 22 | iter.next_separator_eq_or_err('{')?; 23 | let mut sequence = Self { 24 | fields: Vec::default(), 25 | extension_after: None, 26 | }; 27 | 28 | loop { 29 | let continues = if iter.next_is_separator_and_eq('}') { 30 | false 31 | } else if iter.next_is_separator_and_eq('.') { 32 | iter.next_separator_eq_or_err('.')?; 33 | iter.next_separator_eq_or_err('.')?; 34 | let field_len = sequence.fields.len(); 35 | sequence.extension_after = Some(field_len.saturating_sub(1)); 36 | 37 | match iter.next_or_err()? { 38 | token if token.eq_separator(',') => true, 39 | token if token.eq_separator('}') => false, 40 | token => return Err(Error::unexpected_token(token)), 41 | } 42 | } else { 43 | let (field, continues) = Model::>::read_field(iter)?; 44 | sequence.fields.push(field); 45 | continues 46 | }; 47 | 48 | if !continues { 49 | break; 50 | } 51 | } 52 | 53 | Ok(sequence) 54 | } 55 | } 56 | 57 | impl ComponentTypeList { 58 | pub fn try_resolve< 59 | R: Resolver<::SizeType> 60 | + Resolver<::RangeType> 61 | + Resolver<::ConstType> 62 | + Resolver>, 63 | >( 64 | &self, 65 | resolver: &R, 66 | ) -> Result, ResolveError> { 67 | Ok(ComponentTypeList { 68 | fields: self 69 | .fields 70 | .iter() 71 | .map(|f| f.try_resolve(resolver)) 72 | .collect::, _>>()?, 73 | extension_after: self.extension_after, 74 | }) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/basic_enumerated.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | use test_utils::*; 3 | 4 | asn_to_rust!( 5 | r"BasicEnumerated DEFINITIONS AUTOMATIC TAGS ::= 6 | BEGIN 7 | 8 | Basic ::= ENUMERATED { 9 | abc, 10 | def, 11 | ghi 12 | } 13 | 14 | PredefinedNumbers ::= ENUMERATED { 15 | abc(0), 16 | def(5), 17 | ..., -- whatever reserved blubber comment 18 | ghi(8), 19 | jkl(9) 20 | } 21 | 22 | SomeEnum ::= ENUMERATED { 23 | abc(0), 24 | def(1), 25 | ghi(2), 26 | jkl(3), 27 | mno(4), 28 | qrs(15) 29 | } 30 | 31 | 32 | END" 33 | ); 34 | 35 | #[test] 36 | fn test_uper_predefined_numbers() { 37 | assert_eq!((2, vec![0x00_u8]), serialize_uper(&PredefinedNumbers::Abc)); 38 | assert_eq!((2, vec![0x40_u8]), serialize_uper(&PredefinedNumbers::Def)); 39 | assert_eq!((8, vec![0x80_u8]), serialize_uper(&PredefinedNumbers::Ghi)); 40 | assert_eq!((8, vec![0x81_u8]), serialize_uper(&PredefinedNumbers::Jkl)); 41 | 42 | assert_eq!(PredefinedNumbers::Abc, deserialize_uper(&[0x00_u8], 2,)); 43 | assert_eq!(PredefinedNumbers::Def, deserialize_uper(&[0x40_u8], 2,)); 44 | assert_eq!(PredefinedNumbers::Ghi, deserialize_uper(&[0x80_u8], 8,)); 45 | assert_eq!(PredefinedNumbers::Jkl, deserialize_uper(&[0x81_u8], 8,)); 46 | } 47 | 48 | #[test] 49 | fn test_uper_basic_variants_parsed() { 50 | let _abc = Basic::Abc; 51 | let _def = Basic::Def; 52 | let _ghi = Basic::Ghi; 53 | 54 | match Basic::Abc { 55 | // this does not compile if there are additional unexpected variants 56 | Basic::Abc | Basic::Def | Basic::Ghi => {} 57 | } 58 | } 59 | 60 | #[test] 61 | pub fn test_uper_basic() { 62 | let mut writer = UperWriter::default(); 63 | writer.write(&Basic::Abc).unwrap(); 64 | writer.write(&Basic::Def).unwrap(); 65 | writer.write(&Basic::Ghi).unwrap(); 66 | 67 | assert_eq!( 68 | &[ 69 | 0b00 << 6 // Abc 70 | | 0b01 << 4 // Def 71 | | 0b10 << 2 // Ghi 72 | ], 73 | writer.byte_content() 74 | ); 75 | } 76 | 77 | #[test] 78 | fn test_uper_some_enum_with_skipped_numbers() { 79 | serialize_and_deserialize_uper(3, &[0x00], &SomeEnum::Abc); 80 | serialize_and_deserialize_uper(3, &[0x20], &SomeEnum::Def); 81 | serialize_and_deserialize_uper(3, &[0x40], &SomeEnum::Ghi); 82 | serialize_and_deserialize_uper(3, &[0x60], &SomeEnum::Jkl); 83 | serialize_and_deserialize_uper(3, &[0x80], &SomeEnum::Mno); 84 | serialize_and_deserialize_uper(3, &[0xA0], &SomeEnum::Qrs); 85 | } 86 | 87 | #[test] 88 | fn test_der_basic() { 89 | serialize_and_deserialize_der(&[0x0A, 0x01, 0x00], &Basic::Abc); 90 | serialize_and_deserialize_der(&[0x0A, 0x01, 0x01], &Basic::Def); 91 | serialize_and_deserialize_der(&[0x0A, 0x01, 0x02], &Basic::Ghi); 92 | } 93 | -------------------------------------------------------------------------------- /src/descriptor/numbers.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use asn1rs_model::asn::Tag; 3 | use core::marker::PhantomData; 4 | 5 | pub struct Integer = NoConstraint>( 6 | PhantomData, 7 | PhantomData, 8 | ); 9 | 10 | pub trait Number: Copy { 11 | fn to_i64(self) -> i64; 12 | 13 | fn from_i64(value: i64) -> Self; 14 | } 15 | 16 | pub trait Constraint: super::common::Constraint { 17 | // TODO MIN-MAX into RANGE: Option<(T, T)> 18 | const MIN: Option = None; 19 | const MAX: Option = None; 20 | const MIN_T: Option = None; 21 | const MAX_T: Option = None; 22 | const EXTENSIBLE: bool = false; 23 | } 24 | 25 | #[derive(Default)] 26 | pub struct NoConstraint; 27 | impl super::common::Constraint for NoConstraint { 28 | const TAG: Tag = Tag::DEFAULT_INTEGER; 29 | } 30 | impl Constraint for NoConstraint {} 31 | 32 | impl> WritableType for Integer { 33 | type Type = T; 34 | 35 | #[inline] 36 | fn write_value( 37 | writer: &mut W, 38 | value: &Self::Type, 39 | ) -> Result<(), ::Error> { 40 | writer.write_number::(*value) 41 | } 42 | } 43 | 44 | impl> ReadableType for Integer { 45 | type Type = T; 46 | 47 | #[inline] 48 | fn read_value(reader: &mut R) -> Result::Error> { 49 | reader.read_number::() 50 | } 51 | } 52 | 53 | macro_rules! impl_number { 54 | ( $($T:ident),+ ) => {$( 55 | impl Number for $T { 56 | #[inline] 57 | fn to_i64(self) -> i64 { 58 | self as i64 59 | } 60 | 61 | #[inline] 62 | fn from_i64(value: i64) -> Self { 63 | value as $T 64 | } 65 | } 66 | )*} 67 | } 68 | 69 | impl_number!(u8, u16, u32, u64); 70 | impl_number!(i8, i16, i32, i64); 71 | 72 | /* 73 | macro_rules! read_write { 74 | ( $($T:ident),+ ) => {$( 75 | 76 | impl> WritableType for Integer<$T, C> { 77 | type Type = $T; 78 | 79 | #[inline] 80 | fn write_value( 81 | writer: &mut W, 82 | value: &Self::Type, 83 | ) -> Result<(), ::Error> { 84 | paste! { writer.[]::(*value) } 85 | } 86 | } 87 | 88 | impl> ReadableType for Integer<$T, C> { 89 | type Type = $T; 90 | 91 | #[inline] 92 | fn read_value(reader: &mut R) -> Result::Error> { 93 | paste! { reader.[]::() } 94 | } 95 | } 96 | )* 97 | } 98 | } 99 | 100 | read_write!(i8, i16, i32, i64); 101 | read_write!(u8, u16, u32, u64); 102 | */ 103 | -------------------------------------------------------------------------------- /tests/extensible_integer.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"BasicInteger DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | NotRanged ::= Integer 10 | 11 | RangedAndExtensible ::= Integer (0..255,...) 12 | 13 | RangedOptional ::= SEQUENCE { 14 | value Integer (0..255,...) OPTIONAL 15 | } 16 | 17 | END" 18 | ); 19 | 20 | #[asn(transparent)] 21 | #[derive(Default, Debug, Clone, PartialEq, Hash)] 22 | pub struct RangedAndExtensiblePureRust(#[asn(integer(0..255,...))] pub u64); 23 | 24 | #[test] 25 | fn test_extensible_range() { 26 | use asn1rs::descriptor::numbers::Constraint; 27 | assert_eq!( 28 | Some(0_i64), 29 | ___asn1rs_RangedAndExtensiblePureRustField0Constraint::MIN 30 | ); 31 | assert_eq!( 32 | Some(0_u64), 33 | ___asn1rs_RangedAndExtensiblePureRustField0Constraint::MIN_T 34 | ); 35 | assert_eq!( 36 | Some(255_i64), 37 | ___asn1rs_RangedAndExtensiblePureRustField0Constraint::MAX 38 | ); 39 | assert_eq!( 40 | Some(255_u64), 41 | ___asn1rs_RangedAndExtensiblePureRustField0Constraint::MAX_T 42 | ); 43 | assert_eq!( 44 | Some(0_i64), 45 | ___asn1rs_RangedAndExtensibleField0Constraint::MIN 46 | ); 47 | assert_eq!( 48 | Some(0_u64), 49 | ___asn1rs_RangedAndExtensibleField0Constraint::MIN_T 50 | ); 51 | assert_eq!( 52 | Some(255_i64), 53 | ___asn1rs_RangedAndExtensibleField0Constraint::MAX 54 | ); 55 | assert_eq!( 56 | Some(255_u64), 57 | ___asn1rs_RangedAndExtensibleField0Constraint::MAX_T 58 | ); 59 | } 60 | 61 | #[test] 62 | fn test_extensible_flag() { 63 | use asn1rs::descriptor::numbers::Constraint; 64 | assert!(___asn1rs_RangedAndExtensiblePureRustField0Constraint::EXTENSIBLE); 65 | assert!(___asn1rs_RangedAndExtensibleField0Constraint::EXTENSIBLE); 66 | } 67 | 68 | #[test] 69 | fn test_extensible_type() { 70 | let _ = RangedAndExtensible(1024); // does not compile if extensible is ignored 71 | let _ = RangedAndExtensiblePureRust(1024); // does not compile if extensible is ignored 72 | } 73 | 74 | #[test] 75 | fn test_uper_std_0() { 76 | serialize_and_deserialize_uper(9, &[0x00, 0x00], &RangedAndExtensible(0)); 77 | } 78 | 79 | #[test] 80 | fn test_uper_opt_std_0() { 81 | serialize_and_deserialize_uper(10, &[0x80, 0x00], &RangedOptional { value: Some(0) }); 82 | } 83 | 84 | #[test] 85 | fn test_uper_opt_std_254() { 86 | serialize_and_deserialize_uper(10, &[0xBF, 0x80], &RangedOptional { value: Some(254) }); 87 | } 88 | 89 | #[test] 90 | fn test_uper_opt_std_255() { 91 | serialize_and_deserialize_uper(10, &[0xBF, 0xC0], &RangedOptional { value: Some(255) }); 92 | } 93 | 94 | #[test] 95 | fn test_uper_opt_std_256() { 96 | serialize_and_deserialize_uper( 97 | 26, 98 | &[0xC0, 0x80, 0x40, 0x00], 99 | &RangedOptional { value: Some(256) }, 100 | ); 101 | } 102 | -------------------------------------------------------------------------------- /asn1rs-model/src/proc_macro/range.rs: -------------------------------------------------------------------------------- 1 | use syn::buffer::Cursor; 2 | use syn::parse::{Parse, ParseStream}; 3 | use syn::Ident; 4 | use syn::Lit; 5 | use syn::Token; 6 | 7 | #[derive(Debug)] 8 | #[allow(clippy::upper_case_acronyms)] 9 | enum MMV { 10 | MinMax, 11 | Value(i64), 12 | } 13 | 14 | impl MMV { 15 | pub fn try_parse(input: ParseStream) -> syn::Result> { 16 | if let Ok(Lit::Int(int)) = input.parse::() { 17 | Ok(Some(MMV::Value( 18 | int.base10_digits() 19 | .parse::() 20 | .map_err(|_| input.error("Expected int literal for from value of range"))?, 21 | ))) 22 | } else if let Ok(ident) = input.parse::() { 23 | let lc = ident.to_string().to_lowercase(); 24 | if lc == "min" || lc == "max" { 25 | Ok(Some(MMV::MinMax)) 26 | } else { 27 | Err(input.error(format!( 28 | "Invalid identifier, accepted identifiers are: min, max but got: {}", 29 | lc 30 | ))) 31 | } 32 | } else { 33 | Err(input.error("Cannot parse token")) 34 | } 35 | } 36 | } 37 | 38 | #[derive(Debug)] 39 | pub struct IntegerRange(pub Option<(i64, i64)>, pub bool); 40 | 41 | impl Parse for IntegerRange { 42 | fn parse<'a>(input: ParseStream) -> syn::Result { 43 | let min = MMV::try_parse(input)?.ok_or_else(|| input.error("invalid min"))?; 44 | let _ = input.parse::()?; 45 | let _ = input.parse::()?; 46 | let max = MMV::try_parse(input)?.ok_or_else(|| input.error("invalid max"))?; 47 | let extensible = if input.peek(Token![,]) { 48 | let _ = input.parse::()?; 49 | let _ = input.parse::()?; 50 | let _ = input.parse::()?; 51 | let _ = input.parse::()?; 52 | true 53 | } else { 54 | false 55 | }; 56 | 57 | match (min, max) { 58 | (MMV::MinMax, MMV::MinMax) | (MMV::Value(0), MMV::MinMax) => { 59 | Ok(IntegerRange(None, extensible)) 60 | } 61 | (MMV::Value(min), MMV::Value(max)) => Ok(IntegerRange(Some((min, max)), extensible)), 62 | (MMV::MinMax, MMV::Value(max)) => Ok(IntegerRange( 63 | Some(( 64 | if max.is_positive() { 65 | 0 66 | } else { 67 | i64::MAX.wrapping_add(1) 68 | }, 69 | max, 70 | )), 71 | extensible, 72 | )), 73 | (MMV::Value(min), MMV::MinMax) => Ok(IntegerRange(Some((min, i64::MAX)), extensible)), 74 | } 75 | } 76 | } 77 | 78 | pub fn ident_or_literal_or_punct(a: Cursor<'_>) -> Option<(String, Cursor<'_>)> { 79 | a.ident() 80 | .map(|(a, b)| (a.to_string(), b)) 81 | .or_else(|| a.literal().map(|(a, b)| (a.to_string(), b))) 82 | .or_else(|| a.punct().map(|(a, b)| (a.to_string(), b))) 83 | } 84 | -------------------------------------------------------------------------------- /tests/basic_numeric_string.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"BasicBitString DEFINITIONS AUTOMATIC TAGS ::= 9 | BEGIN 10 | 11 | Unconstrained ::= SEQUENCE { 12 | abc NumericString 13 | } 14 | 15 | BasicConstrained ::= SEQUENCE { 16 | abc NumericString (SIZE(8)) 17 | } 18 | 19 | BasicConstrainedSmall ::= SEQUENCE { 20 | abc NumericString (SIZE(4..6)) 21 | } 22 | 23 | BasicConstrainedExtensible ::= SEQUENCE { 24 | abc NumericString (SIZE(4..6,...)) 25 | } 26 | 27 | END" 28 | ); 29 | 30 | #[test] 31 | fn detect_only_invalid_character() { 32 | let mut writer = UperWriter::default(); 33 | let result = Unconstrained { 34 | abc: " 0123456789x".to_string(), 35 | } 36 | .write(&mut writer); 37 | assert_eq!( 38 | Err(asn1rs::protocol::per::ErrorKind::InvalidString( 39 | asn1rs::model::asn::Charset::Numeric, 40 | 'x', 41 | 11 42 | ) 43 | .into()), 44 | result 45 | ) 46 | } 47 | 48 | #[test] 49 | fn test_unconstrained() { 50 | // from playground 51 | serialize_and_deserialize_uper( 52 | 8 * 6 + 4, 53 | &[0x0B, 0x01, 0x23, 0x45, 0x67, 0x89, 0xA0], 54 | &Unconstrained { 55 | abc: " 0123456789".to_string(), 56 | }, 57 | ); 58 | } 59 | 60 | #[test] 61 | fn test_fixed_size() { 62 | // from playground 63 | serialize_and_deserialize_uper( 64 | 8 * 4, 65 | &[0x23, 0x45, 0x67, 0x89], 66 | &BasicConstrained { 67 | abc: "12345678".to_string(), 68 | }, 69 | ); 70 | } 71 | 72 | #[test] 73 | #[should_panic(expected = "SizeNotInRange(8, 4, 6)")] 74 | fn test_too_large() { 75 | // from playground 76 | serialize_and_deserialize_uper( 77 | 0, 78 | &[], 79 | &BasicConstrainedSmall { 80 | abc: "12345678".to_string(), 81 | }, 82 | ); 83 | } 84 | 85 | #[test] 86 | fn test_small_min() { 87 | // from playground 88 | serialize_and_deserialize_uper( 89 | 8 * 2 + 2, 90 | &[0x08, 0xD1, 0x40], 91 | &BasicConstrainedSmall { 92 | abc: "1234".to_string(), 93 | }, 94 | ); 95 | } 96 | 97 | #[test] 98 | fn test_small_max() { 99 | // from playground 100 | serialize_and_deserialize_uper( 101 | 8 * 3 + 2, 102 | &[0x88, 0xD1, 0x59, 0xC0], 103 | &BasicConstrainedSmall { 104 | abc: "123456".to_string(), 105 | }, 106 | ); 107 | } 108 | 109 | #[test] 110 | fn test_extensible_small() { 111 | // from playground 112 | serialize_and_deserialize_uper( 113 | 8 * 2 + 3, 114 | &[0x04, 0x68, 0xA0], 115 | &BasicConstrainedExtensible { 116 | abc: "1234".to_string(), 117 | }, 118 | ); 119 | } 120 | 121 | #[test] 122 | fn test_extensible_extended() { 123 | // from playground 124 | serialize_and_deserialize_uper( 125 | 8 * 4 + 5, 126 | &[0x83, 0x91, 0xA2, 0xB3, 0xC0], 127 | &BasicConstrainedExtensible { 128 | abc: "1234567".to_string(), 129 | }, 130 | ); 131 | } 132 | -------------------------------------------------------------------------------- /asn1rs-model/src/parse/token.rs: -------------------------------------------------------------------------------- 1 | use crate::parse::Location; 2 | use std::fmt::{Display, Formatter}; 3 | 4 | #[derive(Debug, PartialOrd, PartialEq, Eq, Clone)] 5 | pub enum Token { 6 | Text(Location, String), 7 | Separator(Location, char), 8 | } 9 | 10 | impl From for Token { 11 | fn from(separator: char) -> Self { 12 | Token::Separator(Location::default(), separator) 13 | } 14 | } 15 | 16 | impl From for Token { 17 | fn from(text: String) -> Self { 18 | Token::Text(Location::default(), text) 19 | } 20 | } 21 | 22 | impl Display for Token { 23 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 24 | match self { 25 | Token::Text(_, text) => write!(f, "\"{}\"", text), 26 | Token::Separator(_, separator) => write!(f, "\'{}\'", separator), 27 | } 28 | } 29 | } 30 | 31 | impl Token { 32 | pub fn append(self, other: Token) -> (Token, Option) { 33 | match (self, other) { 34 | (Token::Text(location, mut text), Token::Text(_, other)) => ( 35 | Token::Text(location, { 36 | text.push_str(&other); 37 | text 38 | }), 39 | None, 40 | ), 41 | (a, b) => (a, Some(b)), 42 | } 43 | } 44 | 45 | pub fn location(&self) -> Location { 46 | match self { 47 | Token::Text(location, _) => *location, 48 | Token::Separator(location, _) => *location, 49 | } 50 | } 51 | 52 | pub fn eq_text(&self, text: &str) -> bool { 53 | self.text().map(|t| t.eq(text)).unwrap_or(false) 54 | } 55 | 56 | pub fn eq_text_ignore_ascii_case(&self, text: &str) -> bool { 57 | self.text() 58 | .map(|t| t.eq_ignore_ascii_case(text)) 59 | .unwrap_or(false) 60 | } 61 | 62 | pub fn eq_separator(&self, separator: char) -> bool { 63 | self.separator().map(|s| s == separator).unwrap_or(false) 64 | } 65 | 66 | pub fn text(&self) -> Option<&str> { 67 | match self { 68 | Token::Text(_, text) => Some(text), 69 | _ => None, 70 | } 71 | } 72 | 73 | pub fn separator(&self) -> Option { 74 | match self { 75 | Token::Separator(_, char) => Some(*char), 76 | _ => None, 77 | } 78 | } 79 | 80 | pub fn is_text(&self) -> bool { 81 | self.text().is_some() 82 | } 83 | 84 | pub fn is_separator(&self) -> bool { 85 | self.separator().is_some() 86 | } 87 | 88 | pub fn into_text(self) -> Option { 89 | if let Token::Text(_, text) = self { 90 | Some(text) 91 | } else { 92 | None 93 | } 94 | } 95 | 96 | pub fn into_text_or_else E>(self, f: F) -> Result { 97 | match self { 98 | Token::Text(_, text) => Ok(text), 99 | token => Err(f(token)), 100 | } 101 | } 102 | 103 | pub fn into_separator_or_else E>(self, f: F) -> Result { 104 | match self { 105 | Token::Separator(_, separator) => Ok(separator), 106 | token => Err(f(token)), 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tests/basic_bitstring.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use asn1rs::descriptor::bitstring::BitVec; 6 | use test_utils::*; 7 | 8 | asn_to_rust!( 9 | r"BasicBitString DEFINITIONS AUTOMATIC TAGS ::= 10 | BEGIN 11 | 12 | Unconstrained ::= SEQUENCE { 13 | abc BIT STRING 14 | } 15 | 16 | BasicConstrained ::= SEQUENCE { 17 | abc BIT STRING (SIZE(8)) 18 | } 19 | 20 | BasicConstrainedSmall ::= SEQUENCE { 21 | abc BIT STRING (SIZE(4..6)) 22 | } 23 | 24 | BasicConstrainedExtensible ::= SEQUENCE { 25 | abc BIT STRING (SIZE(4..6,...)) 26 | } 27 | 28 | SomeContainer ::= SEQUENCE { 29 | some-value BIT STRING { 30 | very-important-flag (0), 31 | not-so-important-flag(1) 32 | } (SIZE(2)) 33 | } 34 | 35 | END" 36 | ); 37 | 38 | #[test] 39 | fn test_some_container_flag_set() { 40 | let mut c = SomeContainer { 41 | some_value: BitVec::with_len(2), 42 | }; 43 | c.some_value 44 | .set_bit(SomeContainer::SOME_VALUE_VERY_IMPORTANT_FLAG); 45 | serialize_and_deserialize_uper(2, &[0x80], &c); 46 | } 47 | 48 | #[test] 49 | fn test_unconstrained_6_bits() { 50 | // from playground 51 | serialize_and_deserialize_uper( 52 | 14, 53 | &[0x06, 0xAC], 54 | &Unconstrained { 55 | abc: BitVec::from_bytes(vec![0b1010_1100], 6), 56 | }, 57 | ); 58 | } 59 | 60 | #[test] 61 | fn test_unconstrained_5_bytes() { 62 | // from playground 63 | serialize_and_deserialize_uper( 64 | 48, 65 | &[0x28, 0x12, 0x34, 0x56, 0x78, 0x90], 66 | &Unconstrained { 67 | abc: BitVec::from_all_bytes(vec![0x12, 0x34, 0x56, 0x78, 0x90]), 68 | }, 69 | ); 70 | } 71 | 72 | #[test] 73 | fn test_fixed_size() { 74 | // from playground 75 | serialize_and_deserialize_uper( 76 | 8, 77 | &[0x12], 78 | &BasicConstrained { 79 | abc: BitVec::from_all_bytes(vec![0x12]), 80 | }, 81 | ); 82 | } 83 | 84 | #[test] 85 | #[should_panic(expected = "SizeNotInRange(8, 4, 6)")] 86 | fn test_too_large() { 87 | // from playground 88 | serialize_and_deserialize_uper( 89 | 8, 90 | &[0x12], 91 | &BasicConstrainedSmall { 92 | abc: BitVec::from_all_bytes(vec![0x12]), 93 | }, 94 | ); 95 | } 96 | 97 | #[test] 98 | fn test_small_max() { 99 | // from playground 100 | serialize_and_deserialize_uper( 101 | 8, 102 | &[0xBF], 103 | &BasicConstrainedSmall { 104 | abc: BitVec::from_bytes(vec![0xff], 6), 105 | }, 106 | ); 107 | } 108 | 109 | #[test] 110 | fn test_extensible_small() { 111 | // from playground 112 | serialize_and_deserialize_uper( 113 | 9, 114 | &[0x55, 0x80], 115 | &BasicConstrainedExtensible { 116 | abc: BitVec::from_bytes(vec![0xaf], 6), 117 | }, 118 | ); 119 | } 120 | 121 | #[test] 122 | fn test_extensible_extended_1() { 123 | // from playground 124 | serialize_and_deserialize_uper( 125 | 16, 126 | &[0x83, 0xD6], 127 | &BasicConstrainedExtensible { 128 | abc: BitVec::from_bytes(vec![0b1010_1100], 7), 129 | }, 130 | ); 131 | } 132 | 133 | #[test] 134 | fn test_extensible_extended_7() { 135 | // from playground 136 | serialize_and_deserialize_uper( 137 | 23, 138 | &[0x87, 0x56, 0xAC], 139 | &BasicConstrainedExtensible { 140 | abc: BitVec::from_bytes(vec![0b1010_1101, 0b0101_1000], 14), 141 | }, 142 | ); 143 | } 144 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/integer.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::peekable::PeekableTokens; 2 | use crate::asn::{Asn, Range}; 3 | use crate::model::Model; 4 | use crate::parse::Error; 5 | use crate::parse::Token; 6 | use crate::resolve::{Error as ResolveError, LitOrRef}; 7 | use crate::resolve::{ResolveState, Resolver, TryResolve, Unresolved}; 8 | use std::convert::TryFrom; 9 | use std::fmt::{Debug, Display}; 10 | use std::iter::Peekable; 11 | 12 | #[derive(Default, Debug, Clone, PartialOrd, PartialEq, Eq)] 13 | pub struct Integer { 14 | pub range: Range>, 15 | pub constants: Vec<(String, i64)>, 16 | } 17 | 18 | impl Integer { 19 | pub fn with_range(range: Range>) -> Self { 20 | Self { 21 | range, 22 | constants: Vec::default(), 23 | } 24 | } 25 | } 26 | 27 | impl> TryFrom<&mut Peekable> 28 | for Integer<::RangeType> 29 | { 30 | type Error = Error; 31 | 32 | fn try_from(iter: &mut Peekable) -> Result { 33 | let constants = 34 | Model::::maybe_read_constants(iter, Model::::constant_i64_parser)?; 35 | let range = if iter.next_is_separator_and_eq('(') { 36 | let start = iter.next_or_err()?; 37 | iter.next_separator_eq_or_err('.')?; 38 | iter.next_separator_eq_or_err('.')?; 39 | let end = iter.next_or_err()?; 40 | let extensible = if iter.next_is_separator_and_eq(',') { 41 | iter.next_separator_eq_or_err('.')?; 42 | iter.next_separator_eq_or_err('.')?; 43 | iter.next_separator_eq_or_err('.')?; 44 | true 45 | } else { 46 | false 47 | }; 48 | iter.next_separator_eq_or_err(')')?; 49 | let start = start 50 | .text() 51 | .filter(|txt| !txt.eq_ignore_ascii_case("MIN")) 52 | .map(|t| match t.parse::() { 53 | Ok(lit) => LitOrRef::Lit(lit), 54 | Err(_) => LitOrRef::Ref(t.to_string()), 55 | }); 56 | 57 | let end = end 58 | .text() 59 | .filter(|txt| !txt.eq_ignore_ascii_case("MAX")) 60 | .map(|t| match t.parse::() { 61 | Ok(lit) => LitOrRef::Lit(lit), 62 | Err(_) => LitOrRef::Ref(t.to_string()), 63 | }); 64 | 65 | match (start, end) { 66 | (Some(LitOrRef::Lit(0)), None) | (None, Some(LitOrRef::Lit(i64::MAX))) => { 67 | Range(None, None, extensible) 68 | } 69 | (start, end) => Range(start, end, extensible), 70 | } 71 | } else { 72 | Range(None, None, false) 73 | }; 74 | Ok(Self { range, constants }) 75 | } 76 | } 77 | 78 | impl TryResolve> for Integer> { 79 | fn try_resolve(&self, resolver: &impl Resolver) -> Result, ResolveError> { 80 | Ok(Integer { 81 | range: Range( 82 | self.range 83 | .0 84 | .as_ref() 85 | .map(|lor| resolver.resolve(lor)) 86 | .transpose()?, 87 | self.range 88 | .1 89 | .as_ref() 90 | .map(|lor| resolver.resolve(lor)) 91 | .transpose()?, 92 | self.range.2, 93 | ), 94 | //.reconsider_constraints(), 95 | constants: self.constants.clone(), 96 | }) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/basic_ia5string.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"BasicBitString DEFINITIONS AUTOMATIC TAGS ::= 9 | BEGIN 10 | 11 | Unconstrained ::= SEQUENCE { 12 | abc IA5String 13 | } 14 | 15 | BasicConstrained ::= SEQUENCE { 16 | abc IA5String (SIZE(8)) 17 | } 18 | 19 | BasicConstrainedSmall ::= SEQUENCE { 20 | abc IA5String (SIZE(4..6)) 21 | } 22 | 23 | BasicConstrainedExtensible ::= SEQUENCE { 24 | abc IA5String (SIZE(4..6,...)) 25 | } 26 | 27 | END" 28 | ); 29 | 30 | #[test] 31 | fn detect_only_invalid_character() { 32 | let mut writer = asn1rs::prelude::UperWriter::default(); 33 | let result = Unconstrained { 34 | abc: "\u{00}\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}\u{08}\u{09}\u{0A}\u{0B}\u{0C}\u{0D}\u{0E}\u{0F}\u{10}\u{11}\u{12}\u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\u{1A}\u{1B}\u{1C}\u{1D}\u{1E}\u{1F} !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{7F}\u{80}" 35 | .to_string(), 36 | } 37 | .write(&mut writer); 38 | assert_eq!( 39 | Err(asn1rs::protocol::per::ErrorKind::InvalidString( 40 | asn1rs::model::asn::Charset::Ia5, 41 | '\u{80}', 42 | 128 43 | ) 44 | .into()), 45 | result 46 | ) 47 | } 48 | 49 | #[test] 50 | fn test_unconstrained() { 51 | // from playground 52 | serialize_and_deserialize_uper( 53 | 8 * 12 + 3, 54 | &[ 55 | 0x0D, 0xEB, 0xBB, 0x1E, 0xFD, 0xDC, 0xFA, 0x72, 0xC3, 0xA7, 0x76, 0x5C, 0x80, 56 | ], 57 | &Unconstrained { 58 | abc: "unconstrained".to_string(), 59 | }, 60 | ); 61 | } 62 | 63 | #[test] 64 | fn test_fixed_size() { 65 | // from playground 66 | serialize_and_deserialize_uper( 67 | 8 * 7, 68 | &[0xCB, 0xE3, 0x0E, 0x3E, 0x9B, 0x3C, 0xB8], 69 | &BasicConstrained { 70 | abc: "exactly8".to_string(), 71 | }, 72 | ); 73 | } 74 | 75 | #[test] 76 | #[should_panic(expected = "SizeNotInRange(8, 4, 6)")] 77 | fn test_too_large() { 78 | // from playground 79 | serialize_and_deserialize_uper( 80 | 0, 81 | &[], 82 | &BasicConstrainedSmall { 83 | abc: "exactly8".to_string(), 84 | }, 85 | ); 86 | } 87 | 88 | #[test] 89 | fn test_small_min() { 90 | // from playground 91 | serialize_and_deserialize_uper( 92 | 8 * 3 + 6, 93 | &[0x33, 0x6F, 0xEB, 0xC8], 94 | &BasicConstrainedSmall { 95 | abc: "four".to_string(), 96 | }, 97 | ); 98 | } 99 | 100 | #[test] 101 | fn test_small_max() { 102 | // from playground 103 | serialize_and_deserialize_uper( 104 | 8 * 5 + 4, 105 | &[0xB9, 0xAD, 0xD2, 0xB7, 0xC2, 0x10], 106 | &BasicConstrainedSmall { 107 | abc: "s-i-x!".to_string(), 108 | }, 109 | ); 110 | } 111 | 112 | #[test] 113 | fn test_extensible_small() { 114 | // from playground 115 | serialize_and_deserialize_uper( 116 | 8 * 3 + 7, 117 | &[0x19, 0xB7, 0xF5, 0xE4], 118 | &BasicConstrainedExtensible { 119 | abc: "four".to_string(), 120 | }, 121 | ); 122 | } 123 | 124 | #[test] 125 | fn test_extensible_extended() { 126 | // from playground 127 | serialize_and_deserialize_uper( 128 | 8 * 7 + 2, 129 | &[0x83, 0xF3, 0xCB, 0xDB, 0x2E, 0xE4, 0x28, 0x40], 130 | &BasicConstrainedExtensible { 131 | abc: "seven!!".to_string(), 132 | }, 133 | ); 134 | } 135 | -------------------------------------------------------------------------------- /tests/basic_set.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use asn1rs::model::asn::Tag; 6 | use test_utils::*; 7 | 8 | asn_to_rust!( 9 | r"BasicSet DEFINITIONS AUTOMATIC TAGS ::= 10 | BEGIN 11 | 12 | Basic ::= [5] SET { 13 | abc [APPLICATION 7] UTF8String, 14 | def INTEGER 15 | } 16 | 17 | Extensible ::= [5] SET { 18 | abc [APPLICATION 7] UTF8String, 19 | def INTEGER, 20 | ..., 21 | jkl [APPLICATION 3] UTF8String, 22 | ghi [APPLICATION 5] UTF8String 23 | } 24 | 25 | ImplicitNoReorder ::= SET { 26 | abc UTF8String, 27 | def INTEGER 28 | } 29 | 30 | END" 31 | ); 32 | 33 | #[test] 34 | fn test_basic() { 35 | // from playground 36 | serialize_and_deserialize_uper( 37 | 8 * 15, 38 | &[ 39 | // serialization order def -> abc 40 | 0x02, 0x03, 0x0A, 0x0B, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 41 | ], 42 | &Basic { 43 | def: 778, 44 | abc: "hello world".to_string(), 45 | }, 46 | ); 47 | } 48 | 49 | #[test] 50 | fn test_extensible() { 51 | // from playground 52 | serialize_and_deserialize_uper( 53 | 8 * 22 + 2, 54 | &[ 55 | // serialization order def -> abc -> jkl -> ghi 56 | 0x81, 0x01, 0x83, 0x03, 0xB1, 0x3C, 0xB2, 0x90, 0x31, 0x3C, 0xB2, 0x81, 0xC1, 0x00, 57 | 0xDA, 0x9A, 0xDB, 0x01, 0x00, 0xD9, 0xDA, 0x1A, 0x40, 58 | ], 59 | &Extensible { 60 | def: 774, 61 | abc: "bye bye".to_string(), 62 | jkl: Some("jkl".to_string()), 63 | ghi: Some("ghi".to_string()), 64 | }, 65 | ); 66 | } 67 | 68 | #[test] 69 | fn test_extensible_2() { 70 | // from playground 71 | serialize_and_deserialize_uper( 72 | 8 * 11 + 1, 73 | &[ 74 | // serialization order def -> abc -> jkl -> ghi 75 | 0x01, 0x01, 0x83, 0x03, 0xB1, 0x3C, 0xB2, 0x90, 0x31, 0x3C, 0xB2, 0x80, 76 | ], 77 | &Extensible { 78 | def: 774, 79 | abc: "bye bye".to_string(), 80 | jkl: None, 81 | ghi: None, 82 | }, 83 | ); 84 | } 85 | 86 | /// ```asn 87 | /// Extensible ::= { 88 | /// abc "bye bye", 89 | /// def 774, 90 | /// jkl "jkl" 91 | /// } 92 | /// ``` 93 | #[test] 94 | fn test_extensible_4() { 95 | // from playground 96 | serialize_and_deserialize_uper( 97 | 8 * 17 + 2, 98 | &[ 99 | 0x81, 0x01, 0x83, 0x03, 0xB1, 0x3C, 0xB2, 0x90, 0x31, 0x3C, 0xB2, 0x81, 0x81, 0x00, 100 | 0xDA, 0x9A, 0xDB, 0x00, 101 | ], 102 | &Extensible { 103 | def: 774, 104 | abc: "bye bye".to_string(), 105 | jkl: Some("jkl".to_string()), 106 | ghi: None, 107 | }, 108 | ); 109 | } 110 | 111 | #[test] 112 | fn test_implicit_tag_assignment() { 113 | use asn1rs::descriptor::common::Constraint; 114 | 115 | // implicit tagging, therefore no reordering 116 | assert_eq!( 117 | Tag::ContextSpecific(0), 118 | ___asn1rs_ImplicitNoReorderFieldAbcConstraint::TAG 119 | ); 120 | assert_eq!( 121 | Tag::ContextSpecific(1), 122 | ___asn1rs_ImplicitNoReorderFieldDefConstraint::TAG 123 | ); 124 | } 125 | 126 | #[test] 127 | fn test_implicit_no_reorder() { 128 | serialize_and_deserialize_uper( 129 | 8 * 8, 130 | &[0x05, 0x62, 0x65, 0x72, 0x6E, 0x64, 0x01, 0x37], 131 | &ImplicitNoReorder { 132 | abc: "bernd".to_string(), 133 | def: 55, 134 | }, 135 | ) 136 | } 137 | -------------------------------------------------------------------------------- /tests/protobuf_choice.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufChoice ::= SEQUENCE { 10 | some-choice CHOICE { 11 | A INTEGER, 12 | B BOOLEAN, 13 | C UTF8String 14 | } 15 | } 16 | 17 | ProtobufChoiceExt ::= SEQUENCE { 18 | lone-bool BOOLEAN, 19 | some-choice CHOICE { 20 | A INTEGER, 21 | B BOOLEAN, 22 | C UTF8String 23 | }, 24 | lone-int INTEGER 25 | } 26 | 27 | ProtobufOuterChoice ::= CHOICE { 28 | A INTEGER, 29 | B BOOLEAN, 30 | C UTF8String 31 | } 32 | 33 | 34 | END" 35 | ); 36 | 37 | #[test] 38 | #[cfg(feature = "protobuf")] 39 | fn test_choice_a() { 40 | serialize_and_deserialize_protobuf( 41 | &[10, 2, 8, 123], 42 | &ProtobufChoice { 43 | some_choice: ProtobufChoiceSomeChoice::A(123_u64), 44 | }, 45 | ) 46 | } 47 | 48 | #[test] 49 | #[cfg(feature = "protobuf")] 50 | fn test_choice_b() { 51 | serialize_and_deserialize_protobuf( 52 | &[10, 2, 16, 0], 53 | &ProtobufChoice { 54 | some_choice: ProtobufChoiceSomeChoice::B(false), 55 | }, 56 | ) 57 | } 58 | 59 | #[test] 60 | #[cfg(feature = "protobuf")] 61 | fn test_choice_c() { 62 | serialize_and_deserialize_protobuf( 63 | &[10, 3, 26, 1, 99], 64 | &ProtobufChoice { 65 | some_choice: ProtobufChoiceSomeChoice::C("c".into()), 66 | }, 67 | ) 68 | } 69 | 70 | #[test] 71 | #[cfg(feature = "protobuf")] 72 | fn test_choice_ext_a() { 73 | serialize_and_deserialize_protobuf( 74 | &[8, 0, 18, 2, 8, 42, 24, 149, 6], 75 | &ProtobufChoiceExt { 76 | lone_bool: false, 77 | some_choice: ProtobufChoiceExtSomeChoice::A(42), 78 | lone_int: 789_u64, 79 | }, 80 | ) 81 | } 82 | 83 | #[test] 84 | #[cfg(feature = "protobuf")] 85 | fn test_choice_ext_a_shortened() { 86 | assert_eq!( 87 | deserialize_protobuf::(&[18, 2, 8, 42, 24, 149, 6],), 88 | ProtobufChoiceExt { 89 | lone_bool: false, 90 | some_choice: ProtobufChoiceExtSomeChoice::A(42), 91 | lone_int: 789_u64, 92 | }, 93 | ); 94 | } 95 | 96 | #[test] 97 | #[cfg(feature = "protobuf")] 98 | fn test_choice_ext_b() { 99 | serialize_and_deserialize_protobuf( 100 | &[8, 0, 18, 2, 16, 1, 24, 149, 6], 101 | &ProtobufChoiceExt { 102 | lone_bool: false, 103 | some_choice: ProtobufChoiceExtSomeChoice::B(true), 104 | lone_int: 789_u64, 105 | }, 106 | ) 107 | } 108 | 109 | #[test] 110 | #[cfg(feature = "protobuf")] 111 | fn test_choice_ext_b_shortened() { 112 | assert_eq!( 113 | deserialize_protobuf::(&[18, 2, 16, 1, 24, 149, 6],), 114 | ProtobufChoiceExt { 115 | lone_bool: false, 116 | some_choice: ProtobufChoiceExtSomeChoice::B(true), 117 | lone_int: 789_u64, 118 | } 119 | ); 120 | } 121 | 122 | #[test] 123 | #[cfg(feature = "protobuf")] 124 | fn test_choice_outer_a() { 125 | serialize_and_deserialize_protobuf(&[8, 250, 6], &ProtobufOuterChoice::A(890_u64)) 126 | } 127 | 128 | #[test] 129 | #[cfg(feature = "protobuf")] 130 | fn test_choice_outer_b() { 131 | serialize_and_deserialize_protobuf(&[16, 1], &ProtobufOuterChoice::B(true)) 132 | } 133 | 134 | #[test] 135 | #[cfg(feature = "protobuf")] 136 | fn test_choice_outer_c() { 137 | serialize_and_deserialize_protobuf( 138 | &[26, 11, 111, 117, 116, 101, 114, 32, 115, 112, 97, 99, 101], 139 | &ProtobufOuterChoice::C("outer space".into()), 140 | ) 141 | } 142 | -------------------------------------------------------------------------------- /src/protocol/basic/err.rs: -------------------------------------------------------------------------------- 1 | use asn1rs_model::asn::Tag; 2 | use backtrace::Backtrace; 3 | use std::fmt::{Debug, Display, Formatter}; 4 | use std::ops::Range; 5 | 6 | pub struct Error(pub(crate) Box); 7 | 8 | impl Error { 9 | #[inline] 10 | pub fn kind(&self) -> &ErrorKind { 11 | &self.0.kind 12 | } 13 | 14 | #[cold] 15 | #[inline(never)] 16 | pub fn unexpected_tag(expected: Tag, got: Tag) -> Self { 17 | Self::from(ErrorKind::UnexpectedTypeTag { expected, got }) 18 | } 19 | 20 | #[cold] 21 | #[inline(never)] 22 | pub fn unexpected_length(expected: Range, got: u64) -> Self { 23 | Self::from(ErrorKind::UnexpectedTypeLength { expected, got }) 24 | } 25 | 26 | #[cold] 27 | #[inline(never)] 28 | pub fn unexpected_choice_index(expected: Range, got: u64) -> Self { 29 | Self::from(ErrorKind::UnexpectedChoiceIndex { expected, got }) 30 | } 31 | 32 | #[cold] 33 | #[inline(never)] 34 | pub fn unsupported_byte_len(max: u8, got: u8) -> Self { 35 | Self::from(ErrorKind::UnsupportedByteLen { max, got }) 36 | } 37 | } 38 | 39 | impl From for Error { 40 | #[inline] 41 | fn from(kind: ErrorKind) -> Self { 42 | Error(Box::new(Inner::from(kind))) 43 | } 44 | } 45 | 46 | impl From for Error { 47 | #[inline] 48 | fn from(e: std::io::Error) -> Self { 49 | Self::from(ErrorKind::IoError(e)) 50 | } 51 | } 52 | 53 | impl Debug for Error { 54 | #[inline] 55 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 56 | Display::fmt(self, f) 57 | } 58 | } 59 | 60 | impl Display for Error { 61 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 62 | writeln!(f, "{}", self.0.kind)?; 63 | let mut backtrace = self.0.backtrace.clone(); 64 | backtrace.resolve(); 65 | writeln!(f, "{backtrace:?}") 66 | } 67 | } 68 | 69 | impl std::error::Error for Error { 70 | fn description(&self) -> &str { 71 | "encoding or decoding with basic rules failed" 72 | } 73 | } 74 | 75 | #[derive(Debug)] 76 | pub(crate) struct Inner { 77 | pub(crate) kind: ErrorKind, 78 | pub(crate) backtrace: Backtrace, 79 | } 80 | 81 | impl From for Inner { 82 | #[inline] 83 | fn from(kind: ErrorKind) -> Self { 84 | Self { 85 | kind, 86 | backtrace: Backtrace::new_unresolved(), 87 | } 88 | } 89 | } 90 | 91 | #[derive(Debug)] 92 | pub enum ErrorKind { 93 | UnexpectedTypeTag { expected: Tag, got: Tag }, 94 | UnexpectedTypeLength { expected: Range, got: u64 }, 95 | UnexpectedChoiceIndex { expected: Range, got: u64 }, 96 | UnsupportedByteLen { max: u8, got: u8 }, 97 | IoError(std::io::Error), 98 | } 99 | 100 | impl Display for ErrorKind { 101 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 102 | match self { 103 | ErrorKind::UnexpectedTypeTag { expected, got } => { 104 | write!(f, "Expected tag {expected:?} but got {got:?}") 105 | } 106 | ErrorKind::UnexpectedTypeLength { expected, got } => { 107 | write!(f, "Expected length in range {expected:?} but got {got:?}") 108 | } 109 | ErrorKind::UnexpectedChoiceIndex { expected, got } => { 110 | write!(f, "Expected choice index in {expected:?} but got {got:?}") 111 | } 112 | ErrorKind::UnsupportedByteLen { max, got } => { 113 | write!( 114 | f, 115 | "Unsupported byte length received, max={max:?} but got {got:?}" 116 | ) 117 | } 118 | ErrorKind::IoError(e) => { 119 | write!(f, "Experienced underlying IO error: {e:?}") 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /tests/protobuf_sequence_of.rs: -------------------------------------------------------------------------------- 1 | mod test_utils; 2 | 3 | use test_utils::*; 4 | 5 | asn_to_rust!( 6 | r"MyDef DEFINITIONS AUTOMATIC TAGS ::= 7 | BEGIN 8 | 9 | ProtobufSequenceOf ::= SEQUENCE { 10 | many-sint32 SEQUENCE OF INTEGER (-2147483648..2147483647) 11 | } 12 | 13 | ProtobufSequenceOfExt ::= SEQUENCE { 14 | lone-bool BOOLEAN, 15 | many-sint32 SEQUENCE OF INTEGER (-2147483648..2147483647), 16 | another-string UTF8String 17 | } 18 | 19 | ProtobufSequenceOfExtOpt ::= SEQUENCE { 20 | lone-bool BOOLEAN, 21 | many-sint32 SEQUENCE OF INTEGER (-2147483648..2147483647) OPTIONAL, 22 | another-string UTF8String 23 | } 24 | 25 | END" 26 | ); 27 | 28 | #[test] 29 | #[cfg(feature = "protobuf")] 30 | fn test_sequence_of_empty() { 31 | serialize_and_deserialize_protobuf( 32 | &[], 33 | &ProtobufSequenceOf { 34 | many_sint32: Vec::default(), 35 | }, 36 | ) 37 | } 38 | 39 | #[test] 40 | #[cfg(feature = "protobuf")] 41 | fn test_sequence_of_empty_ext() { 42 | serialize_and_deserialize_protobuf( 43 | &[8, 1, 26, 5, 101, 109, 112, 116, 121], 44 | &ProtobufSequenceOfExt { 45 | lone_bool: true, 46 | many_sint32: Vec::default(), 47 | another_string: "empty".into(), 48 | }, 49 | ) 50 | } 51 | 52 | #[test] 53 | #[cfg(feature = "protobuf")] 54 | fn test_sequence_of_single() { 55 | serialize_and_deserialize_protobuf( 56 | &[8, 1], 57 | &ProtobufSequenceOf { 58 | many_sint32: vec![-1_i32], 59 | }, 60 | ) 61 | } 62 | 63 | #[test] 64 | #[cfg(feature = "protobuf")] 65 | fn test_sequence_of_single_ext() { 66 | serialize_and_deserialize_protobuf( 67 | &[8, 0, 16, 1, 26, 6, 115, 105, 110, 103, 108, 101], 68 | &ProtobufSequenceOfExt { 69 | lone_bool: false, 70 | many_sint32: vec![-1_i32], 71 | another_string: "single".into(), 72 | }, 73 | ) 74 | } 75 | 76 | #[test] 77 | #[cfg(feature = "protobuf")] 78 | fn test_sequence_of_multiple() { 79 | serialize_and_deserialize_protobuf( 80 | &[8, 1, 8, 4, 8, 6, 8, 8, 8, 128, 16, 8, 255, 143, 226, 9], 81 | &ProtobufSequenceOf { 82 | many_sint32: vec![-1_i32, 2, 3, 4, 1024, -1024_1024], 83 | }, 84 | ) 85 | } 86 | 87 | #[test] 88 | #[cfg(feature = "protobuf")] 89 | fn test_sequence_of_multiple_ext() { 90 | serialize_and_deserialize_protobuf( 91 | &[ 92 | 8, 0, 16, 1, 16, 4, 16, 6, 16, 8, 16, 128, 16, 16, 255, 143, 226, 9, 26, 8, 109, 117, 93 | 108, 116, 105, 112, 108, 101, 94 | ], 95 | &ProtobufSequenceOfExt { 96 | lone_bool: false, 97 | many_sint32: vec![-1_i32, 2, 3, 4, 1024, -1024_1024], 98 | another_string: "multiple".into(), 99 | }, 100 | ) 101 | } 102 | 103 | #[test] 104 | #[cfg(feature = "protobuf")] 105 | fn test_sequence_of_multiple_ext_opt_some() { 106 | serialize_and_deserialize_protobuf( 107 | &[ 108 | 8, 0, 16, 1, 16, 4, 16, 6, 16, 8, 16, 128, 16, 16, 255, 143, 226, 9, 26, 8, 109, 117, 109 | 108, 116, 105, 112, 108, 101, 110 | ], 111 | &ProtobufSequenceOfExtOpt { 112 | lone_bool: false, 113 | many_sint32: Some(vec![-1_i32, 2, 3, 4, 1024, -1024_1024]), 114 | another_string: "multiple".into(), 115 | }, 116 | ) 117 | } 118 | 119 | #[test] 120 | #[cfg(feature = "protobuf")] 121 | fn test_sequence_of_multiple_ext_opt_none() { 122 | serialize_and_deserialize_protobuf( 123 | &[8, 0, 26, 8, 109, 117, 108, 116, 105, 112, 108, 101], 124 | &ProtobufSequenceOfExtOpt { 125 | lone_bool: false, 126 | many_sint32: None, 127 | another_string: "multiple".into(), 128 | }, 129 | ) 130 | } 131 | -------------------------------------------------------------------------------- /tests/basic_printable_string.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"BasicBitString DEFINITIONS AUTOMATIC TAGS ::= 9 | BEGIN 10 | 11 | Unconstrained ::= SEQUENCE { 12 | abc PrintableString 13 | } 14 | 15 | BasicConstrained ::= SEQUENCE { 16 | abc PrintableString (SIZE(8)) 17 | } 18 | 19 | BasicConstrainedSmall ::= SEQUENCE { 20 | abc PrintableString (SIZE(4..6)) 21 | } 22 | 23 | BasicConstrainedExtensible ::= SEQUENCE { 24 | abc PrintableString (SIZE(4..6,...)) 25 | } 26 | 27 | END" 28 | ); 29 | 30 | #[test] 31 | fn detect_only_invalid_character() { 32 | let mut writer = UperWriter::default(); 33 | let result = Unconstrained { 34 | abc: " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!" 35 | .to_string(), 36 | } 37 | .write(&mut writer); 38 | assert_eq!( 39 | Err(asn1rs::protocol::per::ErrorKind::InvalidString( 40 | asn1rs::model::asn::Charset::Printable, 41 | '!', 42 | 74 43 | ) 44 | .into()), 45 | result 46 | ) 47 | } 48 | 49 | #[test] 50 | fn test_unconstrained() { 51 | // from playground 52 | serialize_and_deserialize_uper( 53 | 8 * 65 + 6, 54 | &[ 55 | 0x4A, 0x40, 0x9D, 0x42, 0x95, 0x6B, 0x16, 0xAE, 0x5E, 0xC1, 0x8B, 0x26, 0x6D, 0x1A, 56 | 0xB6, 0x6E, 0xE1, 0xCB, 0xA7, 0xAF, 0xE0, 0xC2, 0x87, 0x12, 0x2C, 0x68, 0xF2, 0x24, 57 | 0xCA, 0x97, 0x32, 0x6C, 0xE9, 0xF4, 0x28, 0xD2, 0xA7, 0x52, 0xAD, 0x6A, 0xF6, 0x2C, 58 | 0xDA, 0xC3, 0x8B, 0x1E, 0x4C, 0xB9, 0xB3, 0xE8, 0xD3, 0xAB, 0x5E, 0xCD, 0xBB, 0xB7, 59 | 0xF0, 0xE3, 0xCB, 0x9F, 0x4E, 0xBD, 0xBB, 0xF8, 0xF3, 0xE8, 60 | ], 61 | &Unconstrained { 62 | abc: " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 63 | .to_string(), 64 | }, 65 | ); 66 | } 67 | 68 | #[test] 69 | fn test_fixed_size() { 70 | // from playground 71 | serialize_and_deserialize_uper( 72 | 8 * 7, 73 | &[0x62, 0xC9, 0x9B, 0x46, 0xAD, 0x9B, 0xB8], 74 | &BasicConstrained { 75 | abc: "12345678".to_string(), 76 | }, 77 | ); 78 | } 79 | 80 | #[test] 81 | #[should_panic(expected = "SizeNotInRange(8, 4, 6)")] 82 | fn test_too_large() { 83 | // from playground 84 | serialize_and_deserialize_uper( 85 | 0, 86 | &[], 87 | &BasicConstrainedSmall { 88 | abc: "12345678".to_string(), 89 | }, 90 | ); 91 | } 92 | 93 | #[test] 94 | fn test_small_min() { 95 | // from playground 96 | serialize_and_deserialize_uper( 97 | 8 * 3 + 6, 98 | &[0x18, 0xB2, 0x66, 0xD0], 99 | &BasicConstrainedSmall { 100 | abc: "1234".to_string(), 101 | }, 102 | ); 103 | } 104 | 105 | #[test] 106 | fn test_small_max() { 107 | // from playground 108 | serialize_and_deserialize_uper( 109 | 8 * 5 + 4, 110 | &[0x98, 0xB2, 0x66, 0xD1, 0xAB, 0x60], 111 | &BasicConstrainedSmall { 112 | abc: "123456".to_string(), 113 | }, 114 | ); 115 | } 116 | 117 | #[test] 118 | fn test_extensible_small() { 119 | // from playground 120 | serialize_and_deserialize_uper( 121 | 8 * 3 + 7, 122 | &[0x0C, 0x59, 0x33, 0x68], 123 | &BasicConstrainedExtensible { 124 | abc: "1234".to_string(), 125 | }, 126 | ); 127 | } 128 | 129 | #[test] 130 | fn test_extensible_extended() { 131 | // from playground 132 | serialize_and_deserialize_uper( 133 | 8 * 7 + 2, 134 | &[0x83, 0xB1, 0x64, 0xCD, 0xA3, 0x56, 0xCD, 0xC0], 135 | &BasicConstrainedExtensible { 136 | abc: "1234567".to_string(), 137 | }, 138 | ); 139 | } 140 | -------------------------------------------------------------------------------- /src/protocol/protobuf/peq.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::BitVec; 2 | 3 | /// In protobuf default-ish-values - such as '0' for numbers - might be serialized as `null`/`None` 4 | /// if this is possible in the current context. [`ProtobufEq`] will consider these values as equal 5 | /// while the strict implementations of [`PartialEq`] and [`Eq`] will consider them as not equal. 6 | /// 7 | /// ```rust 8 | /// use asn1rs::prelude::*; 9 | /// 10 | /// // behaviour is equal to (Partial-)Eq in non-optional scenarios 11 | /// assert_eq!(0_u64.protobuf_eq(&0_u64), 0_u64.eq(&0)); 12 | /// assert_eq!(1_u64.protobuf_eq(&0), 1_u64.eq(&0)); 13 | /// 14 | /// // behaviour might differ from (Partial-)Eq in optional scenarios 15 | /// assert_ne!(Some(0_u64).protobuf_eq(&None), Some(0_u64).eq(&None)); 16 | /// assert_ne!(Some(String::default()).protobuf_eq(&None), Some(String::default()).eq(&None)); 17 | /// 18 | /// // behaviour might not differ from (Partial-)Eq in optional scenarios 19 | /// assert_eq!(Some(0_u64).protobuf_eq(&Some(0)), Some(0_u64).eq(&Some(0))); 20 | /// assert_eq!(Some(1_u64).protobuf_eq(&None), Some(1_u64).eq(&None)); 21 | /// ``` 22 | pub trait ProtobufEq { 23 | /// Checks whether this and the other value are equal for the protobuf protocol, which considers 24 | /// default-ish values and `null`/`None` as equal. 25 | fn protobuf_eq(&self, other: &Rhs) -> bool; 26 | 27 | /// Inverse of [`ProtobufEq::protobuf_eq`] 28 | fn protobuf_ne(&self, other: &Rhs) -> bool { 29 | !self.protobuf_eq(other) 30 | } 31 | } 32 | 33 | impl ProtobufEq> for Option { 34 | fn protobuf_eq(&self, other: &Option) -> bool { 35 | match self { 36 | Some(ref v) => match other { 37 | Some(ref v_other) => v.protobuf_eq(v_other), 38 | None => v == &T::default(), 39 | }, 40 | None => match other { 41 | Some(ref v_other) => &T::default() == v_other, 42 | None => true, 43 | }, 44 | } 45 | } 46 | } 47 | 48 | impl ProtobufEq> for Vec { 49 | fn protobuf_eq(&self, other: &Vec) -> bool { 50 | if self.len() == other.len() { 51 | for (i, v) in self.iter().enumerate() { 52 | if !other[i].protobuf_eq(v) { 53 | return false; 54 | } 55 | } 56 | true 57 | } else { 58 | false 59 | } 60 | } 61 | } 62 | 63 | impl ProtobufEq for BitVec { 64 | fn protobuf_eq(&self, other: &BitVec) -> bool { 65 | self.eq(other) 66 | } 67 | } 68 | 69 | impl ProtobufEq for bool { 70 | fn protobuf_eq(&self, other: &Self) -> bool { 71 | self == other 72 | } 73 | } 74 | 75 | impl ProtobufEq for u8 { 76 | fn protobuf_eq(&self, other: &Self) -> bool { 77 | self == other 78 | } 79 | } 80 | 81 | impl ProtobufEq for u16 { 82 | fn protobuf_eq(&self, other: &Self) -> bool { 83 | self == other 84 | } 85 | } 86 | 87 | impl ProtobufEq for u32 { 88 | fn protobuf_eq(&self, other: &Self) -> bool { 89 | self == other 90 | } 91 | } 92 | 93 | impl ProtobufEq for u64 { 94 | fn protobuf_eq(&self, other: &Self) -> bool { 95 | self == other 96 | } 97 | } 98 | 99 | impl ProtobufEq for i8 { 100 | fn protobuf_eq(&self, other: &Self) -> bool { 101 | self == other 102 | } 103 | } 104 | 105 | impl ProtobufEq for i16 { 106 | fn protobuf_eq(&self, other: &Self) -> bool { 107 | self == other 108 | } 109 | } 110 | 111 | impl ProtobufEq for i32 { 112 | fn protobuf_eq(&self, other: &Self) -> bool { 113 | self == other 114 | } 115 | } 116 | 117 | impl ProtobufEq for i64 { 118 | fn protobuf_eq(&self, other: &Self) -> bool { 119 | self == other 120 | } 121 | } 122 | 123 | impl ProtobufEq for String { 124 | fn protobuf_eq(&self, other: &Self) -> bool { 125 | self == other 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tests/basic_visible_string.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"BasicBitString DEFINITIONS AUTOMATIC TAGS ::= 9 | BEGIN 10 | 11 | Unconstrained ::= SEQUENCE { 12 | abc VisibleString 13 | } 14 | 15 | BasicConstrained ::= SEQUENCE { 16 | abc VisibleString (SIZE(8)) 17 | } 18 | 19 | BasicConstrainedSmall ::= SEQUENCE { 20 | abc VisibleString (SIZE(4..6)) 21 | } 22 | 23 | BasicConstrainedExtensible ::= SEQUENCE { 24 | abc VisibleString (SIZE(4..6,...)) 25 | } 26 | 27 | END" 28 | ); 29 | 30 | #[test] 31 | fn detect_only_invalid_character() { 32 | let mut writer = UperWriter::default(); 33 | let result = Unconstrained { 34 | abc: " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{7F}" 35 | .to_string(), 36 | } 37 | .write(&mut writer); 38 | assert_eq!( 39 | Err(asn1rs::protocol::per::ErrorKind::InvalidString( 40 | asn1rs::model::asn::Charset::Visible, 41 | '\u{7F}', 42 | 95 43 | ) 44 | .into()), 45 | result 46 | ) 47 | } 48 | 49 | #[test] 50 | fn test_unconstrained() { 51 | // from playground 52 | serialize_and_deserialize_uper( 53 | 8 * 84 + 1, 54 | &[ 55 | 0x5f, 0x40, 0x85, 0x12, 0x34, 0x89, 0x53, 0x27, 0x50, 0xa5, 0x52, 0xb5, 0x8b, 0x57, 0x2f, 56 | 0x60, 0xc5, 0x93, 0x36, 0x8d, 0x5b, 0x37, 0x70, 0xe5, 0xd3, 0xb7, 0x8f, 0x5f, 0x3f, 0x81, 57 | 0x06, 0x14, 0x38, 0x91, 0x63, 0x47, 0x91, 0x26, 0x54, 0xb9, 0x93, 0x67, 0x4f, 0xa1, 0x46, 58 | 0x95, 0x3a, 0x95, 0x6b, 0x57, 0xb1, 0x66, 0xd5, 0xbb, 0x97, 0x6f, 0x5f, 0xc1, 0x87, 0x16, 59 | 0x3c, 0x99, 0x73, 0x67, 0xd1, 0xa7, 0x56, 0xbd, 0x9b, 0x77, 0x6f, 0xe1, 0xc7, 0x97, 0x3e, 60 | 0x9d, 0x7b, 0x77, 0xf1, 0xe7, 0xd7, 0xbf, 0x9f, 0x7f, 0x00 61 | ], 62 | &Unconstrained { 63 | abc: " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" 64 | .to_string(), 65 | }, 66 | ); 67 | } 68 | 69 | #[test] 70 | fn test_fixed_size() { 71 | // from playground 72 | serialize_and_deserialize_uper( 73 | 8 * 7, 74 | &[0x62, 0xC9, 0x9B, 0x46, 0xAD, 0x9B, 0xB8], 75 | &BasicConstrained { 76 | abc: "12345678".to_string(), 77 | }, 78 | ); 79 | } 80 | 81 | #[test] 82 | #[should_panic(expected = "SizeNotInRange(8, 4, 6)")] 83 | fn test_too_large() { 84 | // from playground 85 | serialize_and_deserialize_uper( 86 | 0, 87 | &[], 88 | &BasicConstrainedSmall { 89 | abc: "12345678".to_string(), 90 | }, 91 | ); 92 | } 93 | 94 | #[test] 95 | fn test_small_min() { 96 | // from playground 97 | serialize_and_deserialize_uper( 98 | 8 * 3 + 6, 99 | &[0x18, 0xB2, 0x66, 0xD0], 100 | &BasicConstrainedSmall { 101 | abc: "1234".to_string(), 102 | }, 103 | ); 104 | } 105 | 106 | #[test] 107 | fn test_small_max() { 108 | // from playground 109 | serialize_and_deserialize_uper( 110 | 8 * 5 + 4, 111 | &[0x98, 0xB2, 0x66, 0xD1, 0xAB, 0x60], 112 | &BasicConstrainedSmall { 113 | abc: "123456".to_string(), 114 | }, 115 | ); 116 | } 117 | 118 | #[test] 119 | fn test_extensible_small() { 120 | // from playground 121 | serialize_and_deserialize_uper( 122 | 8 * 3 + 7, 123 | &[0x0C, 0x59, 0x33, 0x68], 124 | &BasicConstrainedExtensible { 125 | abc: "1234".to_string(), 126 | }, 127 | ); 128 | } 129 | 130 | #[test] 131 | fn test_extensible_extended() { 132 | // from playground 133 | serialize_and_deserialize_uper( 134 | 8 * 7 + 2, 135 | &[0x83, 0xB1, 0x64, 0xCD, 0xA3, 0x56, 0xCD, 0xC0], 136 | &BasicConstrainedExtensible { 137 | abc: "1234567".to_string(), 138 | }, 139 | ); 140 | } 141 | -------------------------------------------------------------------------------- /src/converter.rs: -------------------------------------------------------------------------------- 1 | use asn1rs_model::asn::MultiModuleResolver; 2 | use asn1rs_model::generate::rust::RustCodeGenerator as RustGenerator; 3 | use asn1rs_model::generate::Generator; 4 | use asn1rs_model::parse::Tokenizer; 5 | use asn1rs_model::Model; 6 | use std::collections::HashMap; 7 | use std::path::Path; 8 | 9 | #[derive(Debug)] 10 | pub enum Error { 11 | RustGenerator, 12 | #[cfg(feature = "protobuf")] 13 | ProtobufGenerator(asn1rs_model::generate::protobuf::Error), 14 | Model(asn1rs_model::parse::Error), 15 | Io(std::io::Error), 16 | ResolveFailure(asn1rs_model::resolve::Error), 17 | } 18 | 19 | #[cfg(feature = "protobuf")] 20 | impl From for Error { 21 | fn from(g: asn1rs_model::generate::protobuf::Error) -> Self { 22 | Error::ProtobufGenerator(g) 23 | } 24 | } 25 | 26 | impl From for Error { 27 | fn from(m: asn1rs_model::parse::Error) -> Self { 28 | Error::Model(m) 29 | } 30 | } 31 | 32 | impl From for Error { 33 | fn from(e: std::io::Error) -> Self { 34 | Error::Io(e) 35 | } 36 | } 37 | 38 | impl From for Error { 39 | fn from(e: asn1rs_model::resolve::Error) -> Self { 40 | Error::ResolveFailure(e) 41 | } 42 | } 43 | 44 | #[derive(Default)] 45 | pub struct Converter { 46 | models: MultiModuleResolver, 47 | } 48 | 49 | impl Converter { 50 | pub fn load_file>(&mut self, file: F) -> Result<(), Error> { 51 | let input = ::std::fs::read_to_string(file)?; 52 | let tokens = Tokenizer.parse(&input); 53 | let model = Model::try_from(tokens)?; 54 | self.models.push(model); 55 | Ok(()) 56 | } 57 | 58 | pub fn to_rust, A: Fn(&mut RustGenerator)>( 59 | &self, 60 | directory: D, 61 | custom_adjustments: A, 62 | ) -> Result>, Error> { 63 | let models = self.models.try_resolve_all()?; 64 | let scope = models.iter().collect::>(); 65 | let mut files = HashMap::with_capacity(models.len()); 66 | 67 | for model in &models { 68 | let mut generator = RustGenerator::default(); 69 | generator.add_model(model.to_rust_with_scope(&scope[..])); 70 | 71 | custom_adjustments(&mut generator); 72 | 73 | files.insert( 74 | model.name.clone(), 75 | generator 76 | .to_string() 77 | .map_err(|_| Error::RustGenerator)? 78 | .into_iter() 79 | .map(|(file, content)| { 80 | ::std::fs::write(directory.as_ref().join(&file), content)?; 81 | Ok::<_, Error>(file) 82 | }) 83 | .collect::, _>>()?, 84 | ); 85 | } 86 | 87 | Ok(files) 88 | } 89 | 90 | #[cfg(feature = "protobuf")] 91 | pub fn to_protobuf>( 92 | &self, 93 | directory: D, 94 | ) -> Result>, Error> { 95 | use asn1rs_model::protobuf::ToProtobufModel; 96 | 97 | let models = self.models.try_resolve_all()?; 98 | let scope = models.iter().collect::>(); 99 | let mut files = HashMap::with_capacity(models.len()); 100 | 101 | for model in &models { 102 | let mut generator = asn1rs_model::generate::protobuf::ProtobufDefGenerator::default(); 103 | generator.add_model(model.to_rust_with_scope(&scope[..]).to_protobuf()); 104 | 105 | files.insert( 106 | model.name.clone(), 107 | generator 108 | .to_string()? 109 | .into_iter() 110 | .map(|(file, content)| { 111 | ::std::fs::write(directory.as_ref().join(&file), content)?; 112 | Ok::<_, Error>(file) 113 | }) 114 | .collect::, _>>()?, 115 | ); 116 | } 117 | 118 | Ok(files) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/inner_type_constraints.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::peekable::PeekableTokens; 2 | use crate::parse::Error; 3 | use crate::parse::Token; 4 | use std::convert::TryFrom; 5 | use std::iter::Peekable; 6 | 7 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 8 | pub struct InnerTypeConstraints { 9 | implicit_all_present: bool, 10 | entries: Vec<(String, Option, Option)>, 11 | } 12 | 13 | impl> TryFrom<&mut Peekable> for InnerTypeConstraints { 14 | type Error = Error; 15 | 16 | fn try_from(iter: &mut Peekable) -> Result { 17 | iter.next_text_eq_ignore_case_or_err("WITH")?; 18 | iter.next_text_eq_ignore_case_or_err("COMPONENTS")?; 19 | iter.next_separator_eq_or_err('{')?; 20 | 21 | let implicit_all_present = if iter.peek_is_separator_eq('.') { 22 | iter.next_if_separator_and_eq('.')?; 23 | iter.next_if_separator_and_eq('.')?; 24 | iter.next_if_separator_and_eq('.')?; 25 | if iter.peek_is_separator_eq(',') { 26 | iter.next_separator_eq_or_err(',')?; 27 | } 28 | true 29 | } else { 30 | false 31 | }; 32 | 33 | let mut entries = Vec::default(); 34 | 35 | while !iter.peek_is_separator_eq('}') { 36 | let name = iter.next_text_or_err()?; 37 | let vconstr = if iter.peek_is_separator_eq('(') { 38 | iter.next_separator_eq_or_err('(')?; 39 | let result = ValueConstraint::try_from(&mut *iter)?; 40 | iter.next_separator_eq_or_err(')')?; 41 | Some(result) 42 | } else { 43 | None 44 | }; 45 | 46 | let pconstr = if iter.peek_or_err()?.is_text() { 47 | Some(PresenceConstraint::try_from(&mut *iter)?) 48 | } else { 49 | None 50 | }; 51 | 52 | entries.push((name, vconstr, pconstr)); 53 | 54 | if iter.peek_is_separator_eq(',') { 55 | iter.next_separator_eq_or_err(',')?; 56 | } else { 57 | break; 58 | } 59 | } 60 | 61 | iter.next_separator_eq_or_err('}')?; 62 | 63 | Ok(Self { 64 | implicit_all_present, 65 | entries, 66 | }) 67 | } 68 | } 69 | 70 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 71 | pub struct ValueConstraint(String); 72 | 73 | impl> TryFrom<&mut Peekable> for ValueConstraint { 74 | type Error = Error; 75 | 76 | fn try_from(iter: &mut Peekable) -> Result { 77 | let mut level = 0_usize; 78 | let mut string = String::default(); 79 | 80 | // TODO this is a very stupid implementation to just collect all the text within the parenthesis 81 | while !(level == 0 && iter.peek_is_separator_eq(')')) { 82 | match iter.next_or_err()? { 83 | Token::Text(_location, text) => string.push_str(&text), 84 | Token::Separator(_location, separator) => { 85 | match separator { 86 | '(' => level += 1, 87 | ')' => level -= 1, // cannot underflow because of while condition 88 | _ => {} 89 | } 90 | string.push(separator); 91 | } 92 | } 93 | } 94 | 95 | Ok(Self(string)) 96 | } 97 | } 98 | 99 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 100 | pub enum PresenceConstraint { 101 | Present, 102 | Absent, 103 | Optional, 104 | } 105 | 106 | impl> TryFrom<&mut Peekable> for PresenceConstraint { 107 | type Error = Error; 108 | 109 | fn try_from(iter: &mut Peekable) -> Result { 110 | Ok(match iter.next_or_err()? { 111 | t if t.eq_text_ignore_ascii_case("PRESENT") => PresenceConstraint::Present, 112 | t if t.eq_text_ignore_ascii_case("ABSENT") => PresenceConstraint::Absent, 113 | t if t.eq_text_ignore_ascii_case("OPTIONAL") => PresenceConstraint::Optional, 114 | t => return Err(Error::unexpected_token(t)), 115 | }) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /tests/test_utils.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | use asn1rs::prelude::basic::DER; 4 | pub use asn1rs::prelude::*; 5 | 6 | pub fn serialize_uper(to_uper: &impl Writable) -> (usize, Vec) { 7 | let mut writer = UperWriter::default(); 8 | writer.write(to_uper).unwrap(); 9 | let bits = writer.bit_len(); 10 | (bits, writer.into_bytes_vec()) 11 | } 12 | 13 | pub fn deserialize_uper(data: &[u8], bits: usize) -> T { 14 | let mut reader = UperReader::from((data, bits)); 15 | let result = reader.read::().unwrap(); 16 | assert_eq!( 17 | 0, 18 | reader.bits_remaining(), 19 | "After reading, there are still bits remaining!" 20 | ); 21 | result 22 | } 23 | 24 | pub fn serialize_and_deserialize_uper( 25 | bits: usize, 26 | data: &[u8], 27 | uper: &T, 28 | ) { 29 | let serialized = serialize_uper(uper); 30 | assert_eq!( 31 | (bits, data), 32 | (serialized.0, &serialized.1[..]), 33 | "Serialized binary data does not match, bad-hex: {:02x?}", 34 | &serialized.1[..] 35 | ); 36 | assert_eq!( 37 | uper, 38 | &deserialize_uper::(data, bits), 39 | "Deserialized data struct does not match" 40 | ); 41 | } 42 | 43 | pub fn serialize_der(to_der: &impl Writable) -> Vec { 44 | let mut writer = DER::writer(Vec::new()); 45 | writer.write(to_der).unwrap(); 46 | writer.into_inner() 47 | } 48 | 49 | pub fn deserialize_der(data: &[u8]) -> T { 50 | let mut reader = DER::reader(data); 51 | let result = reader.read::().unwrap(); 52 | assert_eq!( 53 | 0, 54 | reader.into_inner().len(), 55 | "After reading, there are still bytes remaining!" 56 | ); 57 | result 58 | } 59 | 60 | pub fn serialize_and_deserialize_der( 61 | data: &[u8], 62 | value: &T, 63 | ) { 64 | let serialized = serialize_der(value); 65 | assert_eq!( 66 | data, 67 | &serialized[..], 68 | "Serialized binary data does not match, bad-hex: {:02x?}", 69 | &serialized[..] 70 | ); 71 | assert_eq!( 72 | value, 73 | &deserialize_der::(data), 74 | "Deserialized data struct does not match" 75 | ); 76 | } 77 | 78 | #[cfg(feature = "protobuf")] 79 | pub fn serialize_protobuf(to_protobuf: &impl Writable) -> Vec { 80 | let mut writer = ProtobufWriter::default(); 81 | writer.write(to_protobuf).unwrap(); 82 | let vec = writer.into_bytes_vec(); 83 | 84 | let mut vec2 = vec![0u8; vec.len()]; 85 | let mut writer2 = ProtobufWriter::from(&mut vec2[..]); 86 | writer2.write(to_protobuf).unwrap(); 87 | 88 | let len_written = writer2.len_written(); 89 | let as_bytes_vec = writer2.as_bytes().to_vec(); 90 | let into_bytes_vec = writer2.into_bytes_vec(); 91 | 92 | assert_eq!( 93 | &vec[..], 94 | &vec2[..], 95 | "ProtobufWriter output differs between Vec and &mut [u8] backend" 96 | ); 97 | 98 | assert_eq!( 99 | &vec[..], 100 | &as_bytes_vec[..], 101 | "ProtobufWriter::as_bytes returns wrong byte slice" 102 | ); 103 | 104 | assert_eq!( 105 | &vec[..], 106 | &into_bytes_vec[..], 107 | "ProtobufWriter::into_bytes_vec returns wrong vec" 108 | ); 109 | 110 | assert_eq!( 111 | vec.len(), 112 | len_written, 113 | "ProtobufWriter::len_written returns wrong value" 114 | ); 115 | 116 | vec 117 | } 118 | 119 | #[cfg(feature = "protobuf")] 120 | pub fn deserialize_protobuf(data: &[u8]) -> T { 121 | let mut reader = ProtobufReader::from(data); 122 | T::read(&mut reader).unwrap() 123 | } 124 | 125 | #[cfg(feature = "protobuf")] 126 | pub fn serialize_and_deserialize_protobuf( 127 | data: &[u8], 128 | proto: &T, 129 | ) { 130 | let serialized = serialize_protobuf(proto); 131 | assert_eq!( 132 | data, 133 | &serialized[..], 134 | "Serialized binary data does not match" 135 | ); 136 | 137 | assert_eq!( 138 | proto, 139 | &deserialize_protobuf::(data), 140 | "Deserialized data struct does not match" 141 | ); 142 | } 143 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/tag_resolver.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::Charset; 2 | use crate::asn::{Asn, Tag, TagProperty, Type}; 3 | use crate::model::{Definition, Model}; 4 | 5 | pub struct TagResolver<'a> { 6 | model: &'a Model, 7 | scope: &'a [&'a Model], 8 | } 9 | 10 | impl TagResolver<'_> { 11 | pub const fn new<'a>(model: &'a Model, scope: &'a [&'a Model]) -> TagResolver<'a> { 12 | TagResolver { model, scope } 13 | } 14 | 15 | pub fn resolve_default(ty: &Type) -> Option { 16 | let model = Model::::default(); 17 | TagResolver { 18 | model: &model, 19 | scope: &[], 20 | } 21 | .resolve_type_tag(ty) 22 | } 23 | 24 | /// ITU-T X.680 | ISO/IEC 8824-1, 8.6 25 | /// ITU-T X.680 | ISO/IEC 8824-1, 41, table 8 26 | pub fn resolve_tag(&self, ty: &str) -> Option { 27 | self.model 28 | .imports 29 | .iter() 30 | .find(|import| import.what.iter().any(|what| what.eq(ty))) 31 | .map(|import| &import.from) 32 | .and_then(|model_name| self.scope.iter().find(|model| model.name.eq(model_name))) 33 | .and_then(|model| { 34 | TagResolver { 35 | model, 36 | scope: self.scope, 37 | } 38 | .resolve_tag(ty) 39 | }) 40 | .or_else(|| { 41 | self.model.definitions.iter().find(|d| d.0.eq(ty)).and_then( 42 | |Definition(_name, asn)| asn.tag.or_else(|| self.resolve_type_tag(&asn.r#type)), 43 | ) 44 | }) 45 | } 46 | 47 | /// ITU-T X.680 | ISO/IEC 8824-1, 8.6 48 | /// ITU-T X.680 | ISO/IEC 8824-1, 41, table 8 49 | pub fn resolve_no_default(&self, ty: &Type) -> Option { 50 | let default = Self::resolve_default(ty); 51 | let resolved = self.resolve_type_tag(ty); 52 | resolved.filter(|r| default.ne(&Some(*r))) 53 | } 54 | 55 | /// ITU-T X.680 | ISO/IEC 8824-1, 8.6 56 | /// ITU-T X.680 | ISO/IEC 8824-1, 41, table 8 57 | pub fn resolve_type_tag(&self, ty: &Type) -> Option { 58 | match ty { 59 | Type::Boolean => Some(Tag::DEFAULT_BOOLEAN), 60 | Type::Integer(_) => Some(Tag::DEFAULT_INTEGER), 61 | Type::BitString(_) => Some(Tag::DEFAULT_BIT_STRING), 62 | Type::OctetString(_) => Some(Tag::DEFAULT_OCTET_STRING), 63 | Type::Enumerated(_) => Some(Tag::DEFAULT_ENUMERATED), 64 | Type::String(_, Charset::Numeric) => Some(Tag::DEFAULT_NUMERIC_STRING), 65 | Type::String(_, Charset::Printable) => Some(Tag::DEFAULT_PRINTABLE_STRING), 66 | Type::String(_, Charset::Visible) => Some(Tag::DEFAULT_VISIBLE_STRING), 67 | Type::String(_, Charset::Utf8) => Some(Tag::DEFAULT_UTF8_STRING), 68 | Type::String(_, Charset::Ia5) => Some(Tag::DEFAULT_IA5_STRING), 69 | Type::Null => Some(Tag::DEFAULT_NULL), 70 | Type::Optional(inner) => self.resolve_type_tag(inner), 71 | Type::Default(inner, ..) => self.resolve_type_tag(inner), 72 | Type::Sequence(_) => Some(Tag::DEFAULT_SEQUENCE), 73 | Type::SequenceOf(_, _) => Some(Tag::DEFAULT_SEQUENCE_OF), 74 | Type::Set(_) => Some(Tag::DEFAULT_SET), 75 | Type::SetOf(_, _) => Some(Tag::DEFAULT_SET_OF), 76 | Type::Choice(choice) => { 77 | let mut tags = choice 78 | .variants() 79 | .take( 80 | choice 81 | .extension_after_index() 82 | .map(|extension_after| extension_after + 1) 83 | .unwrap_or_else(|| choice.len()), 84 | ) 85 | .map(|v| v.tag().or_else(|| self.resolve_type_tag(v.r#type()))) 86 | .collect::>>()?; 87 | tags.sort(); 88 | if cfg!(feature = "debug-proc-macro") { 89 | println!("resolved::::{:?}", tags); 90 | } 91 | tags.into_iter().next() 92 | } 93 | Type::TypeReference(inner, tag) => { 94 | let tag = (*tag).or_else(|| self.resolve_tag(inner.as_str())); 95 | if cfg!(feature = "debug-proc-macro") { 96 | println!("resolved :: {}::Tag = {:?}", inner, tag); 97 | } 98 | tag 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /tests/basic_utf8string.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | 3 | mod test_utils; 4 | 5 | use test_utils::*; 6 | 7 | asn_to_rust!( 8 | r"BasicBitString DEFINITIONS AUTOMATIC TAGS ::= 9 | BEGIN 10 | 11 | Unconstrained ::= SEQUENCE { 12 | abc UTF8String 13 | } 14 | 15 | BasicConstrained ::= SEQUENCE { 16 | abc UTF8String (SIZE(8)) 17 | } 18 | 19 | BasicConstrainedFixedExtensible ::= SEQUENCE { 20 | abc UTF8String (SIZE(8,...)) 21 | } 22 | 23 | BasicConstrainedSmall ::= SEQUENCE { 24 | abc UTF8String (SIZE(4..6)) 25 | } 26 | 27 | BasicConstrainedExtensible ::= SEQUENCE { 28 | abc UTF8String (SIZE(4..6,...)) 29 | } 30 | 31 | END" 32 | ); 33 | 34 | #[test] 35 | fn test_unconstrained() { 36 | // from playground 37 | serialize_and_deserialize_uper( 38 | 8 * 14, 39 | &[ 40 | 0x0D, 0x75, 0x6E, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6E, 0x65, 0x64, 41 | ], 42 | &Unconstrained { 43 | abc: "unconstrained".to_string(), 44 | }, 45 | ); 46 | } 47 | 48 | #[test] 49 | fn test_fixed_size() { 50 | // from playground 51 | serialize_and_deserialize_uper( 52 | 8 * 9, 53 | &[0x08, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6C, 0x79, 0x38], 54 | &BasicConstrained { 55 | abc: "exactly8".to_string(), 56 | }, 57 | ); 58 | } 59 | 60 | #[test] 61 | fn test_fixed_size_extensible_smaller() { 62 | // from playground 63 | serialize_and_deserialize_uper( 64 | 8 * 4, 65 | &[0x03, 0x6C, 0x74, 0x38], 66 | &BasicConstrainedFixedExtensible { 67 | abc: "lt8".to_string(), 68 | }, 69 | ); 70 | } 71 | 72 | #[test] 73 | fn test_fixed_size_extensible_exact() { 74 | // from playground 75 | serialize_and_deserialize_uper( 76 | 8 * 9, 77 | &[0x08, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6C, 0x79, 0x38], 78 | &BasicConstrainedFixedExtensible { 79 | abc: "exactly8".to_string(), 80 | }, 81 | ); 82 | } 83 | 84 | #[test] 85 | fn test_fixed_size_extensible_greater() { 86 | // from playground 87 | serialize_and_deserialize_uper( 88 | 8 * 10, 89 | &[0x09, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6C, 0x79, 0x5F, 0x39], 90 | &BasicConstrainedFixedExtensible { 91 | abc: "exactly_9".to_string(), 92 | }, 93 | ); 94 | } 95 | 96 | #[test] 97 | #[should_panic(expected = "SizeNotInRange(8, 4, 6)")] 98 | fn test_too_large() { 99 | // from playground 100 | serialize_and_deserialize_uper( 101 | 0, 102 | &[], 103 | &BasicConstrainedSmall { 104 | abc: "exactly8".to_string(), 105 | }, 106 | ); 107 | } 108 | 109 | #[test] 110 | fn test_small_min() { 111 | // from playground 112 | serialize_and_deserialize_uper( 113 | 8 * 5, 114 | &[0x04, 0x66, 0x6F, 0x75, 0x72], 115 | &BasicConstrainedSmall { 116 | abc: "four".to_string(), 117 | }, 118 | ); 119 | } 120 | 121 | #[test] 122 | fn test_small_max() { 123 | // from playground 124 | serialize_and_deserialize_uper( 125 | 8 * 7, 126 | &[0x06, 0x73, 0x2D, 0x69, 0x2D, 0x78, 0x21], 127 | &BasicConstrainedSmall { 128 | abc: "s-i-x!".to_string(), 129 | }, 130 | ); 131 | } 132 | 133 | #[test] 134 | fn test_extensible_small() { 135 | // from playground 136 | serialize_and_deserialize_uper( 137 | 8 * 5, 138 | &[0x04, 0x66, 0x6F, 0x75, 0x72], 139 | &BasicConstrainedExtensible { 140 | abc: "four".to_string(), 141 | }, 142 | ); 143 | } 144 | 145 | #[test] 146 | fn test_extensible_extended() { 147 | // from playground 148 | serialize_and_deserialize_uper( 149 | 8 * 8, 150 | &[0x07, 0x73, 0x65, 0x76, 0x65, 0x6E, 0x21, 0x21], 151 | &BasicConstrainedExtensible { 152 | abc: "seven!!".to_string(), 153 | }, 154 | ); 155 | } 156 | 157 | #[test] 158 | fn test_single_umlaut() { 159 | serialize_and_deserialize_uper( 160 | 3 * 8, 161 | &[0x02, 0xC3, 0xA4], 162 | &Unconstrained { 163 | abc: "ä".to_string(), 164 | }, 165 | ) 166 | } 167 | 168 | #[test] 169 | fn test_multiple_umlauts() { 170 | serialize_and_deserialize_uper( 171 | 15 * 8, 172 | &[ 173 | 0x0E, 0xC3, 0xA4, 0xC3, 0xB6, 0xC3, 0xBC, 0xC3, 0x84, 0xC3, 0x96, 0xC3, 0x9C, 0xC3, 174 | 0x9F, 175 | ], 176 | &Unconstrained { 177 | abc: "äöüÄÖÜß".to_string(), 178 | }, 179 | ) 180 | } 181 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/charset.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::Tag; 2 | 3 | #[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq, EnumString)] 4 | #[strum(serialize_all = "lowercase")] 5 | pub enum Charset { 6 | Utf8, 7 | /// ITU-T X.680 | ISO/IEC 8824-1, 43.3 8 | Numeric, 9 | /// ITU-T X.680 | ISO/IEC 8824-1, 43.3 10 | Printable, 11 | 12 | // /// (Also T61String) 13 | // Teletext, 14 | // Videotext, 15 | /// Encoding as in ISO/IEC 646 (??) 16 | Ia5, 17 | 18 | // GraphicsString, 19 | /// ITU-T X.680 | ISO/IEC 8824-1, 43.3 20 | /// (Also ISO646String) 21 | Visible, 22 | } 23 | 24 | impl Charset { 25 | /// Sorted according to ITU-T X.680, 43.5 26 | /// ```rust 27 | /// use asn1rs_model::asn::Charset; 28 | /// assert!(Charset::NUMERIC_STRING_CHARACTERS.chars().all(|c| Charset::Numeric.is_valid(c))); 29 | /// assert!(Charset::NUMERIC_STRING_CHARACTERS.chars().all(|c| Charset::Utf8.is_valid(c))); 30 | /// assert!(Charset::NUMERIC_STRING_CHARACTERS.chars().all(|c| Charset::Printable.is_valid(c))); 31 | /// assert!(Charset::NUMERIC_STRING_CHARACTERS.chars().all(|c| Charset::Ia5.is_valid(c))); 32 | /// assert!(Charset::NUMERIC_STRING_CHARACTERS.chars().all(|c| Charset::Visible.is_valid(c))); 33 | /// assert_eq!(11, Charset::NUMERIC_STRING_CHARACTERS.chars().count()); 34 | /// ``` 35 | pub const NUMERIC_STRING_CHARACTERS: &'static str = " 0123456789"; 36 | 37 | /// Sorted according to ITU-T X.680, 43.6 38 | /// ```rust 39 | /// use asn1rs_model::asn::Charset; 40 | /// assert!(Charset::PRINTABLE_STRING_CHARACTERS.chars().all(|c| Charset::Printable.is_valid(c))); 41 | /// assert!(Charset::PRINTABLE_STRING_CHARACTERS.chars().all(|c| Charset::Utf8.is_valid(c))); 42 | /// assert!(Charset::PRINTABLE_STRING_CHARACTERS.chars().all(|c| Charset::Ia5.is_valid(c))); 43 | /// assert!(Charset::PRINTABLE_STRING_CHARACTERS.chars().all(|c| Charset::Visible.is_valid(c))); 44 | /// assert_eq!(74, Charset::PRINTABLE_STRING_CHARACTERS.chars().count()); 45 | /// ``` 46 | pub const PRINTABLE_STRING_CHARACTERS: &'static str = 47 | " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 48 | 49 | /// Sorted according to ITU-T X.680, 43.8 50 | /// ```rust 51 | /// use asn1rs_model::asn::Charset; 52 | /// assert!(Charset::IA5_STRING_CHARACTERS.chars().all(|c| Charset::Ia5.is_valid(c))); 53 | /// assert!(Charset::IA5_STRING_CHARACTERS.chars().all(|c| Charset::Utf8.is_valid(c))); 54 | /// assert_eq!(128, Charset::IA5_STRING_CHARACTERS.chars().count()); 55 | /// ``` 56 | pub const IA5_STRING_CHARACTERS: &'static str = 57 | "\u{00}\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}\u{08}\u{09}\u{0A}\u{0B}\u{0C}\u{0D}\u{0E}\u{0F}\u{10}\u{11}\u{12}\u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\u{1A}\u{1B}\u{1C}\u{1D}\u{1E}\u{1F} !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{7F}"; 58 | 59 | /// Sorted according to ITU-T X.680, 43.7 60 | /// ```rust 61 | /// use asn1rs_model::asn::Charset; 62 | /// assert!(Charset::VISIBLE_STRING_CHARACTERS.chars().all(|c| Charset::Visible.is_valid(c))); 63 | /// assert!(Charset::VISIBLE_STRING_CHARACTERS.chars().all(|c| Charset::Ia5.is_valid(c))); 64 | /// assert!(Charset::VISIBLE_STRING_CHARACTERS.chars().all(|c| Charset::Utf8.is_valid(c))); 65 | /// assert_eq!(95, Charset::VISIBLE_STRING_CHARACTERS.chars().count()); 66 | /// ``` 67 | pub const VISIBLE_STRING_CHARACTERS: &'static str = 68 | " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; 69 | 70 | pub fn default_tag(self) -> Tag { 71 | match self { 72 | Charset::Utf8 => Tag::DEFAULT_UTF8_STRING, 73 | Charset::Numeric => Tag::DEFAULT_NUMERIC_STRING, 74 | Charset::Printable => Tag::DEFAULT_PRINTABLE_STRING, 75 | Charset::Ia5 => Tag::DEFAULT_IA5_STRING, 76 | Charset::Visible => Tag::DEFAULT_VISIBLE_STRING, 77 | } 78 | } 79 | 80 | pub fn find_invalid(self, str: &str) -> Option<(usize, char)> { 81 | str.chars() 82 | .enumerate() 83 | .find(|(_index, char)| !self.is_valid(*char)) 84 | } 85 | 86 | pub const fn is_valid(self, char: char) -> bool { 87 | match self { 88 | Charset::Utf8 => true, 89 | Charset::Numeric => matches!(char, ' ' | '0'..='9'), 90 | Charset::Printable => { 91 | matches!(char, ' ' | '\'' ..= ')' | '+' ..= ':' | '=' | '?' | 'A'..='Z' | 'a'..='z' ) 92 | } 93 | Charset::Ia5 => matches!(char as u32, 0_u32..=127), 94 | Charset::Visible => matches!(char as u32, 32_u32..=126), 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /asn1rs-model/src/resolve.rs: -------------------------------------------------------------------------------- 1 | use crate::model::LiteralValue; 2 | use std::fmt::{Debug, Display, Formatter}; 3 | 4 | pub trait ResolveState: Clone { 5 | type SizeType: Display + Debug + Clone + PartialOrd + PartialEq; 6 | type RangeType: Display + Debug + Clone + PartialOrd + PartialEq; 7 | type ConstType: Debug + Clone + PartialOrd + PartialEq; 8 | } 9 | 10 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 11 | pub struct Resolved; 12 | impl ResolveState for Resolved { 13 | type SizeType = usize; 14 | type RangeType = i64; 15 | type ConstType = LiteralValue; 16 | } 17 | 18 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 19 | pub struct Unresolved; 20 | impl ResolveState for Unresolved { 21 | type SizeType = LitOrRef; 22 | type RangeType = LitOrRef; 23 | type ConstType = LitOrRef; 24 | } 25 | 26 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 27 | pub enum LitOrRef { 28 | Lit(T), 29 | Ref(String), 30 | } 31 | 32 | impl Default for LitOrRef 33 | where 34 | T: Default, 35 | { 36 | fn default() -> Self { 37 | LitOrRef::Lit(T::default()) 38 | } 39 | } 40 | 41 | impl Display for LitOrRef 42 | where 43 | T: Display, 44 | { 45 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 46 | match self { 47 | LitOrRef::Lit(v) => Display::fmt(v, f), 48 | LitOrRef::Ref(v) => Display::fmt(v, f), 49 | } 50 | } 51 | } 52 | 53 | #[derive(Debug, PartialOrd, PartialEq, Eq)] 54 | pub enum Error { 55 | FailedToResolveType(String), 56 | FailedToResolveReference(String), 57 | FailedToParseLiteral(String), 58 | } 59 | 60 | impl std::error::Error for Error {} 61 | impl std::fmt::Display for Error { 62 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 63 | match self { 64 | Error::FailedToResolveType(name) => { 65 | write!(f, "Failed to resolve type with name: {}", name) 66 | } 67 | Error::FailedToResolveReference(name) => { 68 | write!(f, "Failed to resolve reference with name: {}", name) 69 | } 70 | Error::FailedToParseLiteral(literal) => { 71 | write!(f, "Failed to parse literal: {}", literal) 72 | } 73 | } 74 | } 75 | } 76 | 77 | pub trait Resolver { 78 | fn resolve(&self, lor: &LitOrRef) -> Result; 79 | } 80 | 81 | pub trait TryResolve { 82 | fn try_resolve(&self, resolver: &impl Resolver) -> Result; 83 | } 84 | 85 | #[cfg(test)] 86 | pub mod tests { 87 | use super::*; 88 | use crate::asn::Integer; 89 | use crate::asn::Range; 90 | use crate::asn::{Asn, Type}; 91 | use crate::model::{Definition, Model, ValueReference}; 92 | 93 | #[test] 94 | fn test_simple_resolve() { 95 | let mut unresolved = Model::> { 96 | name: "UnresolvedModel".to_string(), 97 | definitions: vec![Definition( 98 | "IntegerWithVR".to_string(), 99 | Type::::Integer(Integer { 100 | range: Range( 101 | Some(LitOrRef::Ref("my_min".to_string())), 102 | Some(LitOrRef::Ref("my_max".to_string())), 103 | true, 104 | ), 105 | constants: Vec::default(), 106 | }) 107 | .untagged(), 108 | )], 109 | ..Default::default() 110 | }; 111 | 112 | assert_eq!( 113 | Error::FailedToResolveReference("my_min".to_string()), 114 | unresolved.try_resolve().unwrap_err() 115 | ); 116 | 117 | unresolved.value_references.push(ValueReference { 118 | name: "my_min".to_string(), 119 | role: Type::Integer(Integer::default()).untagged(), 120 | value: LiteralValue::Integer(123), 121 | }); 122 | 123 | assert_eq!( 124 | Error::FailedToResolveReference("my_max".to_string()), 125 | unresolved.try_resolve().unwrap_err() 126 | ); 127 | 128 | unresolved.value_references.push(ValueReference { 129 | name: "my_max".to_string(), 130 | role: Type::Integer(Integer::default()).untagged(), 131 | value: LiteralValue::Integer(456), 132 | }); 133 | 134 | let resolved = unresolved.try_resolve().unwrap(); 135 | assert_eq!( 136 | &resolved.definitions[..], 137 | &[Definition( 138 | "IntegerWithVR".to_string(), 139 | Type::::Integer(Integer { 140 | range: Range(Some(123), Some(456), true), 141 | constants: Vec::default(), 142 | }) 143 | .untagged(), 144 | )] 145 | ) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/peekable.rs: -------------------------------------------------------------------------------- 1 | use crate::parse::ErrorKind; 2 | use crate::parse::Token; 3 | use std::iter::Peekable; 4 | 5 | pub trait PeekableTokens { 6 | fn peek_or_err(&mut self) -> Result<&Token, ErrorKind>; 7 | 8 | fn peek_is_text_eq(&mut self, text: &str) -> bool; 9 | 10 | fn peek_is_text_eq_ignore_case(&mut self, text: &str) -> bool; 11 | 12 | fn peek_is_separator_eq(&mut self, separator: char) -> bool; 13 | 14 | #[inline] 15 | fn peek_is_text_and_satisfies bool>(&mut self, probe: F) -> bool { 16 | self.peek_or_err() 17 | .ok() 18 | .and_then(Token::text) 19 | .map(probe) 20 | .unwrap_or(false) 21 | } 22 | 23 | fn next_or_err(&mut self) -> Result; 24 | 25 | fn next_text_or_err(&mut self) -> Result; 26 | 27 | fn next_text_eq_ignore_case_or_err(&mut self, text: &str) -> Result; 28 | 29 | fn next_text_eq_any_ignore_case_or_err(&mut self, texts: &[&str]) -> Result; 30 | 31 | #[inline] 32 | fn next_is_text_and_eq_ignore_case(&mut self, text: &str) -> bool { 33 | self.next_text_eq_ignore_case_or_err(text).is_ok() 34 | } 35 | 36 | fn next_if_separator_and_eq(&mut self, separator: char) -> Result; 37 | 38 | #[inline] 39 | fn next_separator_eq_or_err(&mut self, separator: char) -> Result<(), ErrorKind> { 40 | self.next_if_separator_and_eq(separator).map(drop) 41 | } 42 | 43 | #[inline] 44 | fn next_is_separator_and_eq(&mut self, separator: char) -> bool { 45 | self.next_separator_eq_or_err(separator).is_ok() 46 | } 47 | } 48 | 49 | impl> PeekableTokens for Peekable { 50 | #[inline] 51 | fn peek_or_err(&mut self) -> Result<&Token, ErrorKind> { 52 | self.peek().ok_or(ErrorKind::UnexpectedEndOfStream) 53 | } 54 | 55 | #[inline] 56 | fn peek_is_text_eq(&mut self, text: &str) -> bool { 57 | self.peek() 58 | .and_then(Token::text) 59 | .map(|t| t.eq(text)) 60 | .unwrap_or(false) 61 | } 62 | 63 | #[inline] 64 | fn peek_is_text_eq_ignore_case(&mut self, text: &str) -> bool { 65 | self.peek() 66 | .and_then(Token::text) 67 | .map(|t| text.eq_ignore_ascii_case(t)) 68 | .unwrap_or(false) 69 | } 70 | 71 | #[inline] 72 | fn peek_is_separator_eq(&mut self, separator: char) -> bool { 73 | self.peek() 74 | .map(|t| t.eq_separator(separator)) 75 | .unwrap_or(false) 76 | } 77 | 78 | #[inline] 79 | fn next_or_err(&mut self) -> Result { 80 | self.next().ok_or(ErrorKind::UnexpectedEndOfStream) 81 | } 82 | 83 | #[inline] 84 | fn next_text_or_err(&mut self) -> Result { 85 | let peeked = self.peek_or_err()?; 86 | if peeked.text().is_some() { 87 | let token = self.next_or_err()?; 88 | debug_assert!(token.text().is_some()); 89 | match token { 90 | Token::Separator(..) => unreachable!(), 91 | Token::Text(_, text) => Ok(text), 92 | } 93 | } else { 94 | Err(ErrorKind::ExpectedText(peeked.clone())) 95 | } 96 | } 97 | 98 | #[inline] 99 | fn next_text_eq_ignore_case_or_err(&mut self, text: &str) -> Result { 100 | let peeked = self.peek_or_err()?; 101 | if peeked.eq_text_ignore_ascii_case(text) { 102 | let token = self.next_or_err()?; 103 | debug_assert!(token.eq_text_ignore_ascii_case(text)); 104 | Ok(token) 105 | } else { 106 | Err(ErrorKind::ExpectedTextGot(text.to_string(), peeked.clone())) 107 | } 108 | } 109 | 110 | #[inline] 111 | fn next_text_eq_any_ignore_case_or_err(&mut self, texts: &[&str]) -> Result { 112 | let peeked = self.peek_or_err()?; 113 | if matches!(peeked, Token::Text(_, token) if texts.iter().any(|text| token.eq_ignore_ascii_case(text))) 114 | { 115 | let token = self.next_or_err()?; 116 | debug_assert!( 117 | matches!(&token, Token::Text(_, token) if texts.iter().any(|text| token.eq_ignore_ascii_case(text))) 118 | ); 119 | Ok(token) 120 | } else { 121 | Err(ErrorKind::UnexpectedToken(peeked.clone())) 122 | } 123 | } 124 | 125 | #[inline] 126 | fn next_if_separator_and_eq(&mut self, separator: char) -> Result { 127 | let peeked = self.peek_or_err()?; 128 | if peeked.eq_separator(separator) { 129 | let token = self.next_or_err()?; 130 | debug_assert!(token.eq_separator(separator)); 131 | Ok(token) 132 | } else { 133 | Err(ErrorKind::ExpectedSeparatorGot(separator, peeked.clone())) 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /asn1rs-model/src/parse/tokenizer.rs: -------------------------------------------------------------------------------- 1 | use crate::parse::{Location, Token}; 2 | 3 | #[derive(Default)] 4 | pub struct Tokenizer; 5 | 6 | impl Tokenizer { 7 | /// Tokenize the given ASN.1 string. 8 | /// Parse the string line by line and character by character. 9 | /// Exclude comments as defined in 12.6.2-4 ITU-T Rec. X.680 (02/2021) 10 | /// Ignore single-line comments defined with "--". 11 | /// Ignore multi-line comments defined with /* */. 12 | /// Comment terminates when a matching "*/" has been found for each "/*" 13 | pub fn parse(&self, asn: &str) -> Vec { 14 | let mut previous = None; 15 | let mut tokens = Vec::new(); 16 | let mut nest_lvl = 0; // Nest level of the comments 17 | 18 | for (line_0, line) in asn.lines().enumerate() { 19 | let mut token = None; 20 | let mut content_iterator = line.chars().enumerate().peekable(); 21 | 22 | while let Some((column_0, char)) = content_iterator.next() { 23 | if nest_lvl > 0 { 24 | match char { 25 | '*' => { 26 | if let Some((_, '/')) = content_iterator.peek() { 27 | nest_lvl -= 1; 28 | content_iterator.next(); // remove closing '/' 29 | } 30 | } 31 | '/' => { 32 | if let Some((_, '*')) = content_iterator.peek() { 33 | nest_lvl += 1; 34 | content_iterator.next(); // remove opening '*' 35 | } 36 | } 37 | _ => { 38 | if content_iterator.peek().is_none() 39 | && line_0 == asn.lines().count() - 1 40 | { 41 | panic!("The file has unclosed comment blocks. Nested comment blocks are counted."); 42 | } else { 43 | continue; 44 | } 45 | } 46 | } 47 | continue; 48 | } 49 | // Get rid of one-line comments. Can also happen immediately after closing block comment 50 | if nest_lvl == 0 51 | && char == '-' 52 | && content_iterator.peek().map(|&(_, ch)| ch) == Some('-') 53 | { 54 | content_iterator.next(); // remove second '-' 55 | break; // ignore rest of the line 56 | } 57 | match char { 58 | '/' if content_iterator.peek().map(|&(_, ch)| ch) == Some('*') => { 59 | content_iterator.next(); // remove opening '*' 60 | nest_lvl += 1; 61 | } 62 | // asn syntax 63 | ':' | ';' | '=' | '(' | ')' | '{' | '}' | '.' | ',' | '[' | ']' | '\'' 64 | | '"' => { 65 | token = Some(Token::Separator( 66 | Location::at(line_0 + 1, column_0 + 1), 67 | char, 68 | )) 69 | } 70 | // text 71 | c if !c.is_control() && c != ' ' => { 72 | token = Some(Token::Text( 73 | Location::at(line_0 + 1, column_0 + 1), 74 | format!("{}", c), 75 | )); 76 | } 77 | // text separator 78 | ' ' | '\r' | '\n' | '\t' => { 79 | if let Some(token) = previous.take() { 80 | tokens.push(token); 81 | } 82 | } 83 | c => eprintln!( 84 | "Ignoring unexpected character: {}-0x{:02x}-{:03}", 85 | c, c as u8, c as u8 86 | ), 87 | } 88 | 89 | if let Some(token) = token.take() { 90 | previous = match previous { 91 | None => Some(token), 92 | Some(current) => { 93 | let (token, second) = current.append(token); 94 | match second { 95 | None => Some(token), 96 | Some(next) => { 97 | tokens.push(token); 98 | Some(next) 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | 106 | if let Some(token) = previous.take() { 107 | tokens.push(token); 108 | } 109 | } 110 | 111 | if let Some(token) = previous { 112 | tokens.push(token); 113 | } 114 | 115 | tokens 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/enumerated.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::peekable::PeekableTokens; 2 | use crate::parse::Error; 3 | use crate::parse::Token; 4 | use std::convert::TryFrom; 5 | use std::iter::Peekable; 6 | 7 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 8 | pub struct Enumerated { 9 | variants: Vec, 10 | extension_after: Option, 11 | } 12 | 13 | impl From> for Enumerated { 14 | fn from(variants: Vec) -> Self { 15 | Self { 16 | variants, 17 | extension_after: None, 18 | } 19 | } 20 | } 21 | 22 | impl Enumerated { 23 | pub fn from_variants(variants: impl Into>) -> Self { 24 | Self { 25 | variants: variants.into(), 26 | extension_after: None, 27 | } 28 | } 29 | 30 | pub fn from_names(variants: impl Iterator) -> Self { 31 | Self { 32 | variants: variants.map(EnumeratedVariant::from_name).collect(), 33 | extension_after: None, 34 | } 35 | } 36 | 37 | pub const fn with_extension_after(mut self, extension_after: usize) -> Self { 38 | self.extension_after = Some(extension_after); 39 | self 40 | } 41 | 42 | pub const fn with_maybe_extension_after(mut self, extension_after: Option) -> Self { 43 | self.extension_after = extension_after; 44 | self 45 | } 46 | 47 | pub fn len(&self) -> usize { 48 | self.variants.len() 49 | } 50 | 51 | pub fn is_empty(&self) -> bool { 52 | self.variants.is_empty() 53 | } 54 | 55 | pub fn variants(&self) -> impl Iterator { 56 | self.variants.iter() 57 | } 58 | 59 | pub fn is_extensible(&self) -> bool { 60 | self.extension_after.is_some() 61 | } 62 | 63 | pub fn extension_after_index(&self) -> Option { 64 | self.extension_after 65 | } 66 | } 67 | 68 | impl> TryFrom<&mut Peekable> for Enumerated { 69 | type Error = Error; 70 | 71 | fn try_from(iter: &mut Peekable) -> Result { 72 | iter.next_separator_eq_or_err('{')?; 73 | let mut enumerated = Self { 74 | variants: Vec::new(), 75 | extension_after: None, 76 | }; 77 | 78 | loop { 79 | if let Ok(extension_marker) = iter.next_if_separator_and_eq('.') { 80 | if enumerated.variants.is_empty() || enumerated.extension_after.is_some() { 81 | return Err(Error::invalid_position_for_extension_marker( 82 | extension_marker, 83 | )); 84 | } else { 85 | iter.next_separator_eq_or_err('.')?; 86 | iter.next_separator_eq_or_err('.')?; 87 | enumerated.extension_after = Some(enumerated.variants.len() - 1); 88 | loop_ctrl_separator!(iter.next_or_err()?); 89 | } 90 | } else { 91 | let variant_name = iter.next_text_or_err()?; 92 | let token = iter.next_or_err()?; 93 | 94 | if token.eq_separator(',') || token.eq_separator('}') { 95 | enumerated 96 | .variants 97 | .push(EnumeratedVariant::from_name(variant_name)); 98 | loop_ctrl_separator!(token); 99 | } else if token.eq_separator('(') { 100 | let token = iter.next_or_err()?; 101 | let number = token 102 | .text() 103 | .and_then(|t| t.parse::().ok()) 104 | .ok_or_else(|| Error::invalid_number_for_enum_variant(token))?; 105 | iter.next_separator_eq_or_err(')')?; 106 | enumerated 107 | .variants 108 | .push(EnumeratedVariant::from_name_number(variant_name, number)); 109 | loop_ctrl_separator!(iter.next_or_err()?); 110 | } else { 111 | loop_ctrl_separator!(token); 112 | } 113 | } 114 | } 115 | 116 | Ok(enumerated) 117 | } 118 | } 119 | 120 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 121 | pub struct EnumeratedVariant { 122 | pub(crate) name: String, 123 | pub(crate) number: Option, 124 | } 125 | 126 | #[cfg(test)] 127 | impl From for EnumeratedVariant { 128 | fn from(s: S) -> Self { 129 | EnumeratedVariant::from_name(s) 130 | } 131 | } 132 | 133 | impl EnumeratedVariant { 134 | pub fn from_name(name: I) -> Self { 135 | Self { 136 | name: name.to_string(), 137 | number: None, 138 | } 139 | } 140 | 141 | pub fn from_name_number(name: I, number: usize) -> Self { 142 | Self { 143 | name: name.to_string(), 144 | number: Some(number), 145 | } 146 | } 147 | 148 | pub const fn with_number(self, number: usize) -> Self { 149 | self.with_number_opt(Some(number)) 150 | } 151 | 152 | pub const fn with_number_opt(mut self, number: Option) -> Self { 153 | self.number = number; 154 | self 155 | } 156 | 157 | pub fn name(&self) -> &str { 158 | &self.name 159 | } 160 | 161 | pub fn number(&self) -> Option { 162 | self.number 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /tests/proc_macro_coverage_hack.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::ToTokens; 3 | use std::io::Read; 4 | use std::panic::AssertUnwindSafe; 5 | use std::path::PathBuf; 6 | use std::str::FromStr; 7 | use std::{env, fs, panic}; 8 | use syn::Attribute; 9 | use syn::ItemEnum; 10 | use syn::ItemStruct; 11 | 12 | #[test] 13 | fn cover_asn_to_rust_macro() { 14 | walk_dir({ 15 | // This code doesn't check much. Instead, it does macro expansion at run time to let 16 | // tarpaulin measure code coverage for the macro. 17 | let mut path = env::current_dir().unwrap(); 18 | path.push("tests"); 19 | path 20 | }); 21 | } 22 | 23 | fn walk_dir(path: PathBuf) { 24 | for entry in fs::read_dir(path).unwrap() { 25 | match entry { 26 | Ok(entry) if entry.file_type().unwrap().is_dir() => { 27 | walk_dir(entry.path()); 28 | } 29 | Ok(entry) 30 | if entry.file_type().unwrap().is_file() 31 | && entry.path().to_str().map_or(false, |s| s.ends_with(".rs")) => 32 | { 33 | println!("Feeding {:?}", entry.path()); 34 | let file = fs::File::open(entry.path()).unwrap(); 35 | emulate_macro_expansion_fallible(file); 36 | } 37 | _ => {} 38 | } 39 | } 40 | } 41 | 42 | /// Based on https://github.com/jeremydavis519/runtime-macros/blob/master/src/lib.rs 43 | /// 44 | /// 45 | /// This awfully great hack allows tarpaulin to track the proc-macro related function 46 | /// calls for a better line coverage. It manually parses each test file, takes the literal 47 | /// String of the 'asn_to_rust!()' macro and generates the models and then rust code for it. 48 | /// It then takes this intermediate result and feeds it to the proc-attribute expander to 49 | /// also track it. 50 | /// 51 | /// WARNING: This does *NO* logic check and does *NOT* unit tests. It just helps to track the 52 | /// called functions and executed lines. 53 | /// 54 | pub fn emulate_macro_expansion_fallible(mut file: fs::File) { 55 | fn ast_parse_str(attr: &str, item: &str) -> TokenStream { 56 | asn1rs_model::proc_macro::parse( 57 | TokenStream::from_str(attr).unwrap(), 58 | TokenStream::from_str(item).unwrap(), 59 | ) 60 | } 61 | 62 | fn asn_to_rust_fn2(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream { 63 | let input = syn::parse2::(input).unwrap(); 64 | let result = asn1rs_model::proc_macro::asn_to_rust(&input.value()); 65 | TokenStream::from_str(&result).unwrap() 66 | } 67 | 68 | fn feed_derive_parser( 69 | attributes: &[Attribute], 70 | attribute_path: &syn::Path, 71 | item: impl Fn() -> String, 72 | body_start_marker: &str, 73 | ) { 74 | for attr in attributes { 75 | if *attr.path() == *attribute_path { 76 | let item = item(); 77 | let header = attr 78 | .parse_args::() 79 | .unwrap() 80 | .to_string(); 81 | let body = { 82 | let body_start = item.find(body_start_marker).unwrap(); 83 | &item[body_start..] 84 | }; 85 | 86 | if cfg!(feature = "debug-proc-macro") { 87 | println!("##########: {}", item); 88 | println!(" header: {}", header); 89 | println!(" body: {}", body); 90 | println!(); 91 | } 92 | 93 | let result = ast_parse_str(&header, body).to_string(); 94 | 95 | if result.contains("compile_error") { 96 | panic!("{}", result); 97 | } else { 98 | syn::parse_str::(&result).expect("Result is invalid"); 99 | } 100 | break; 101 | } 102 | } 103 | } 104 | 105 | struct MacroVisitor { 106 | macro_path: syn::Path, 107 | attribute_path: syn::Path, 108 | } 109 | impl<'ast> syn::visit::Visit<'ast> for MacroVisitor { 110 | fn visit_item_enum(&mut self, i: &'ast ItemEnum) { 111 | feed_derive_parser( 112 | &i.attrs[..], 113 | &self.attribute_path, 114 | || i.into_token_stream().to_string(), 115 | " pub enum ", 116 | ); 117 | } 118 | 119 | fn visit_item_struct(&mut self, i: &'ast ItemStruct) { 120 | feed_derive_parser( 121 | &i.attrs[..], 122 | &self.attribute_path, 123 | || i.into_token_stream().to_string(), 124 | " pub struct ", 125 | ); 126 | } 127 | 128 | fn visit_macro(&mut self, macro_item: &'ast syn::Macro) { 129 | if macro_item.path == self.macro_path { 130 | let result = asn_to_rust_fn2(macro_item.tokens.clone()); 131 | let ast = AssertUnwindSafe(syn::parse_file(&result.to_string()).unwrap()); 132 | syn::visit::visit_file(self, &*ast); 133 | } 134 | } 135 | } 136 | 137 | let mut content = String::new(); 138 | file.read_to_string(&mut content).unwrap(); 139 | 140 | syn::visit::visit_file( 141 | &mut MacroVisitor { 142 | macro_path: syn::parse_str("asn_to_rust").unwrap(), 143 | attribute_path: syn::parse_str("asn").unwrap(), 144 | }, 145 | &syn::parse_file(content.as_str()).unwrap(), 146 | ) 147 | } 148 | -------------------------------------------------------------------------------- /src/descriptor/bitstring.rs: -------------------------------------------------------------------------------- 1 | use crate::descriptor::{ReadableType, Reader, WritableType, Writer}; 2 | use crate::protocol::per::unaligned::BYTE_LEN; 3 | use asn1rs_model::asn::Tag; 4 | use std::cmp::Ordering; 5 | use std::marker::PhantomData; 6 | 7 | pub struct BitString(PhantomData); 8 | 9 | pub trait Constraint: super::common::Constraint { 10 | const MIN: Option = None; 11 | const MAX: Option = None; 12 | const EXTENSIBLE: bool = false; 13 | } 14 | 15 | #[derive(Default)] 16 | pub struct NoConstraint; 17 | impl super::common::Constraint for NoConstraint { 18 | const TAG: Tag = Tag::DEFAULT_BIT_STRING; 19 | } 20 | impl Constraint for NoConstraint {} 21 | 22 | impl WritableType for BitString { 23 | type Type = BitVec; 24 | 25 | #[inline] 26 | fn write_value(writer: &mut W, value: &Self::Type) -> Result<(), W::Error> { 27 | writer.write_bit_string::(value.as_byte_slice(), value.1) 28 | } 29 | } 30 | 31 | impl ReadableType for BitString { 32 | type Type = BitVec; 33 | 34 | #[inline] 35 | fn read_value(reader: &mut R) -> Result::Error> { 36 | let (vec, bit_len) = reader.read_bit_string::()?; 37 | Ok(BitVec(vec, bit_len)) 38 | } 39 | } 40 | 41 | #[derive(Debug, Default, Clone, PartialOrd, PartialEq, Eq, Hash)] 42 | pub struct BitVec(Vec, u64); 43 | 44 | impl BitVec { 45 | pub fn from_all_bytes(bytes: Vec) -> Self { 46 | let bit_len = (bytes.len() * BYTE_LEN) as u64; 47 | Self::from_bytes(bytes, bit_len) 48 | } 49 | 50 | pub fn from_bytes(mut bytes: Vec, bit_len: u64) -> Self { 51 | match (bytes.len() * BYTE_LEN).cmp(&(bit_len as usize)) { 52 | Ordering::Less => { 53 | // fill vec with missing zero-bytes 54 | let missing_bytes = ((bit_len as usize + 7) / BYTE_LEN) - bytes.len(); 55 | bytes.extend(core::iter::repeat(0u8).take(missing_bytes)); 56 | } 57 | Ordering::Equal => { 58 | // nothing to do 59 | } 60 | Ordering::Greater => { 61 | // ensure that unused bits are zero 62 | let mask = 0xFF_u8 >> (bit_len as usize % BYTE_LEN); 63 | let index = bit_len as usize / BYTE_LEN; 64 | bytes[index] &= !mask; 65 | } 66 | } 67 | BitVec(bytes, bit_len) 68 | } 69 | 70 | pub fn with_len(bits: u64) -> Self { 71 | let bytes = (bits as usize + 7) / 8; 72 | BitVec(core::iter::repeat(0u8).take(bytes).collect(), bits) 73 | } 74 | 75 | /// # Panics 76 | /// 77 | /// If the given `Vec` is not at least 4 bytes large 78 | pub fn from_vec_with_trailing_bit_len(mut bytes: Vec) -> Self { 79 | const U64_SIZE: usize = std::mem::size_of::(); 80 | let bytes_position = bytes.len() - U64_SIZE; 81 | let mut bit_len_buffer = [0u8; U64_SIZE]; 82 | bit_len_buffer.copy_from_slice(&bytes[bytes_position..]); 83 | bytes.truncate(bytes_position); 84 | Self(bytes, u64::from_be_bytes(bit_len_buffer)) 85 | } 86 | 87 | pub fn to_vec_with_trailing_bit_len(&self) -> Vec { 88 | let mut buffer = self.0[..(self.1 as usize + (BYTE_LEN - 1)) / BYTE_LEN].to_vec(); 89 | self.1.to_be_bytes().iter().for_each(|b| buffer.push(*b)); 90 | buffer 91 | } 92 | 93 | pub fn is_bit_set(&self, bit: u64) -> bool { 94 | let byte = bit / 8; 95 | let bit = bit % 8; 96 | let mask = 0x80_u8 >> bit; 97 | self.0 98 | .get(byte as usize) 99 | .map(|b| *b & mask != 0) 100 | .unwrap_or(false) 101 | } 102 | 103 | pub fn set_bit(&mut self, bit: u64) { 104 | self.ensure_vec_large_enough(bit + 1); 105 | let byte = bit / 8; 106 | let bit = bit % 8; 107 | let mask = 0x80_u8 >> bit; 108 | self.0[byte as usize] |= mask; 109 | } 110 | 111 | pub fn reset_bit(&mut self, bit: u64) { 112 | self.ensure_vec_large_enough(bit + 1); 113 | let byte = bit / 8; 114 | let bit = bit % 8; 115 | let mask = 0x80_u8 >> bit; 116 | self.0[byte as usize] &= !mask; 117 | } 118 | 119 | fn ensure_vec_large_enough(&mut self, bits: u64) { 120 | if bits > self.1 { 121 | let bytes = ((bits + 7) / 8) as usize; 122 | self.0.resize(bytes, 0x00); 123 | self.1 = bits; 124 | } 125 | } 126 | 127 | pub fn overall_capacity_at_least(&mut self, bits: u64) { 128 | self.ensure_vec_large_enough(bits); 129 | } 130 | 131 | pub fn bit_len(&self) -> u64 { 132 | self.1 133 | } 134 | 135 | pub fn byte_len(&self) -> usize { 136 | self.0.len() 137 | } 138 | 139 | pub fn as_byte_slice(&self) -> &[u8] { 140 | self.0.as_slice() 141 | } 142 | 143 | pub fn split(self) -> (Vec, u64) { 144 | (self.0, self.1) 145 | } 146 | } 147 | 148 | #[cfg(test)] 149 | pub mod tests { 150 | use super::*; 151 | 152 | #[test] 153 | fn trailing_bit_len_repr() { 154 | for bit_len in 0..(BYTE_LEN * 10) { 155 | for value in 0..u8::MAX { 156 | let byte_len = (bit_len + 7) / 8; 157 | let start = BitVec( 158 | core::iter::repeat(value).take(byte_len).collect(), 159 | bit_len as u64, 160 | ); 161 | let vec_repr = start.to_vec_with_trailing_bit_len(); 162 | let end = BitVec::from_vec_with_trailing_bit_len(vec_repr); 163 | assert_eq!(start, end); 164 | } 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /asn1rs-model/src/asn/size.rs: -------------------------------------------------------------------------------- 1 | use crate::asn::peekable::PeekableTokens; 2 | use crate::parse::Error; 3 | use crate::parse::Token; 4 | use crate::resolve::{Error as ResolveError, LitOrRef, Resolver, TryResolve}; 5 | use crate::resolve::{ResolveState, Unresolved}; 6 | use std::convert::TryFrom; 7 | use std::fmt::{Debug, Display}; 8 | use std::iter::Peekable; 9 | 10 | #[derive(Debug, Clone, PartialOrd, PartialEq, Eq)] 11 | pub enum Size { 12 | Any, 13 | Fix(T, bool), 14 | Range(T, T, bool), 15 | } 16 | 17 | impl Size { 18 | pub fn min(&self) -> Option<&T> { 19 | match self { 20 | Size::Any => None, 21 | Size::Fix(min, _) => Some(min), 22 | Size::Range(min, _, _) => Some(min), 23 | } 24 | } 25 | 26 | pub fn max(&self) -> Option<&T> { 27 | match self { 28 | Size::Any => None, 29 | Size::Fix(max, _) => Some(max), 30 | Size::Range(_, max, _) => Some(max), 31 | } 32 | } 33 | 34 | pub fn extensible(&self) -> bool { 35 | match self { 36 | Size::Any => false, 37 | Size::Fix(_, extensible) => *extensible, 38 | Size::Range(_, _, extensible) => *extensible, 39 | } 40 | } 41 | 42 | pub fn to_constraint_string(&self) -> Option { 43 | match self { 44 | Size::Any => None, 45 | Size::Fix(min, extensible) => Some(format!( 46 | "size({}{})", 47 | min, 48 | if *extensible { ",..." } else { "" } 49 | )), 50 | Size::Range(min, max, extensible) => Some(format!( 51 | "size({}..{}{})", 52 | min, 53 | max, 54 | if *extensible { ",..." } else { "" } 55 | )), 56 | } 57 | } 58 | } 59 | 60 | impl Size { 61 | pub fn reconsider_constraints(self) -> Self { 62 | if let Self::Range(min, max, extensible) = self { 63 | if min == 0 && max == i64::MAX as usize && !extensible { 64 | Self::Any 65 | } else if min == max { 66 | Self::Fix(min, extensible) 67 | } else { 68 | self 69 | } 70 | } else { 71 | self 72 | } 73 | } 74 | } 75 | 76 | impl> TryFrom<&mut Peekable> 77 | for Size<::SizeType> 78 | { 79 | type Error = Error; 80 | 81 | fn try_from(iter: &mut Peekable) -> Result { 82 | iter.next_text_eq_ignore_case_or_err("SIZE")?; 83 | iter.next_separator_eq_or_err('(')?; 84 | 85 | let start = iter.next_or_err()?; 86 | let start = start 87 | .text() 88 | .filter(|txt| !txt.eq_ignore_ascii_case("MIN")) 89 | .map(|t| match t.parse::() { 90 | Ok(lit) => LitOrRef::Lit(lit), 91 | Err(_) => LitOrRef::Ref(t.to_string()), 92 | }) 93 | .filter(|lor| LitOrRef::Lit(0).ne(lor)); 94 | 95 | if !iter.peek_is_separator_eq('.') { 96 | match iter.next_or_err()? { 97 | t if t.eq_separator(')') => Ok(Size::Fix(start.unwrap_or_default(), false)), 98 | t if t.eq_separator(',') => { 99 | iter.next_separator_eq_or_err('.')?; 100 | iter.next_separator_eq_or_err('.')?; 101 | iter.next_separator_eq_or_err('.')?; 102 | iter.next_separator_eq_or_err(')')?; 103 | Ok(Size::Fix(start.unwrap_or_default(), true)) 104 | } 105 | t => Err(Error::unexpected_token(t)), 106 | } 107 | } else { 108 | const MAX: usize = i64::MAX as usize; 109 | 110 | iter.next_separator_eq_or_err('.')?; 111 | iter.next_separator_eq_or_err('.')?; 112 | let end = iter.next_or_err()?; 113 | let end = end 114 | .text() 115 | .filter(|txt| !txt.eq_ignore_ascii_case("MAX")) 116 | .map(|t| match t.parse::() { 117 | Ok(lit) => LitOrRef::Lit(lit), 118 | Err(_) => LitOrRef::Ref(t.to_string()), 119 | }) 120 | .filter(|lor| LitOrRef::Lit(MAX).ne(lor)); 121 | 122 | let any = matches!( 123 | (&start, &end), 124 | (None, None) | (Some(LitOrRef::Lit(0)), None) | (None, Some(LitOrRef::Lit(MAX))) 125 | ); 126 | 127 | if any { 128 | iter.next_separator_eq_or_err(')')?; 129 | Ok(Size::Any) 130 | } else { 131 | let start = start.unwrap_or_default(); 132 | let end = end.unwrap_or(LitOrRef::Lit(i64::MAX as usize)); 133 | let extensible = if iter.next_separator_eq_or_err(',').is_ok() { 134 | iter.next_separator_eq_or_err('.')?; 135 | iter.next_separator_eq_or_err('.')?; 136 | iter.next_separator_eq_or_err('.')?; 137 | true 138 | } else { 139 | false 140 | }; 141 | iter.next_separator_eq_or_err(')')?; 142 | if start == end { 143 | Ok(Size::Fix(start, extensible)) 144 | } else { 145 | Ok(Size::Range(start, end, extensible)) 146 | } 147 | } 148 | } 149 | } 150 | } 151 | 152 | impl TryResolve> for Size> { 153 | fn try_resolve(&self, resolver: &impl Resolver) -> Result, ResolveError> { 154 | Ok(match self { 155 | Size::Any => Size::Any, 156 | Size::Fix(len, ext) => Size::Fix(resolver.resolve(len)?, *ext), 157 | Size::Range(min, max, ext) => { 158 | Size::Range(resolver.resolve(min)?, resolver.resolve(max)?, *ext) 159 | } 160 | } 161 | .reconsider_constraints()) 162 | } 163 | } 164 | --------------------------------------------------------------------------------