diff --git a/asn1rs-macros/src/ast/mod.rs b/asn1rs-macros/src/ast/mod.rs index 777a9770..1592d6a3 100644 --- a/asn1rs-macros/src/ast/mod.rs +++ b/asn1rs-macros/src/ast/mod.rs @@ -1,10 +1,12 @@ mod range; mod tag; -use asn1rs_model::model::{Definition, Field, Range, Tag, Type}; +use asn1rs_model::model::{Definition, Field, Model, Range, Tag, Type}; use proc_macro::TokenStream; use quote::quote; use range::MaybeRanged; +use std::str::FromStr; +use syn::export::TokenStream2; use syn::parse::{Parse, ParseBuffer}; use syn::spanned::Spanned; use syn::Item; @@ -18,6 +20,8 @@ pub(crate) fn parse(attr: TokenStream, item: TokenStream) -> TokenStream { let attributes = parse_macro_input!(attr as AttributeArgs); let item = parse_macro_input!(item as Item); + let mut additional_impl: Vec = Vec::default(); + let item = match item { Item::Struct(mut strct) => { let mut fields = Vec::new(); @@ -63,8 +67,19 @@ pub(crate) fn parse(attr: TokenStream, item: TokenStream) -> TokenStream { } } println!("---------- parsed"); - let definition = Definition(strct.ident.to_string(), Type::Sequence(fields)); + let definition = Definition(strct.ident.to_string(), Type::Sequence(fields).untagged()); println!("{:#?}", definition); + let model: Model = Model { + name: "__proc_macro".to_string(), + imports: vec![], + definitions: vec![definition], + }; + let model_rust = model.to_rust(); + + use asn1rs_model::gen::rust::walker::AsnDefWalker; + let stringified = AsnDefWalker::stringify(&model_rust); + additional_impl.push(TokenStream2::from_str(&stringified).unwrap()); + println!("---------- output"); let st = Item::Struct(strct.clone()); println!("{}", TokenStream::from(quote! {#st}).to_string()); @@ -73,7 +88,14 @@ pub(crate) fn parse(attr: TokenStream, item: TokenStream) -> TokenStream { item => item, }; - TokenStream::from(quote! {#item}) + let result = TokenStream::from(quote! { + #item + #(#additional_impl)* + }); + + println!("---------- result"); + println!("{}", result.to_string()); + result } #[derive(Debug, Default)] diff --git a/asn1rs-model/src/gen/rust/mod.rs b/asn1rs-model/src/gen/rust/mod.rs index f6fe1a1d..8e2f4cee 100644 --- a/asn1rs-model/src/gen/rust/mod.rs +++ b/asn1rs-model/src/gen/rust/mod.rs @@ -1,5 +1,6 @@ pub mod protobuf; pub mod uper; +pub mod walker; #[cfg(feature = "psql")] pub mod psql; diff --git a/asn1rs-model/src/gen/rust/walker.rs b/asn1rs-model/src/gen/rust/walker.rs new file mode 100644 index 00000000..47772ef3 --- /dev/null +++ b/asn1rs-model/src/gen/rust/walker.rs @@ -0,0 +1,390 @@ +use crate::gen::rust::GeneratorSupplement; +use crate::gen::RustCodeGenerator; +use crate::model::{Definition, Model, Range, Rust, RustType}; +use codegen::{Block, Impl, Scope}; +use std::fmt::Display; + +pub const CRATE_SYN_PREFIX: &str = "::asn1rs::syn::"; + +pub struct AsnDefWalker; + +impl AsnDefWalker { + fn write_type_definitions( + &self, + scope: &mut Scope, + Definition(name, r#type): &Definition, + ) { + match r#type { + Rust::Struct(fields) => { + scope.raw(&format!( + "type AsnDef{} = {}Sequence<{}>;", + name, CRATE_SYN_PREFIX, name + )); + for (field, r#type) in fields { + self.write_type_declaration(scope, &name, &field, r#type); + } + } + Rust::Enum(_) => {} + Rust::DataEnum(_) => {} + Rust::TupleStruct(_) => {} + } + } + + #[must_use] + pub fn type_declaration(r#type: &RustType, name: &str) -> String { + match r#type { + RustType::Bool => format!("{}Bool", CRATE_SYN_PREFIX), + RustType::I8(_) => format!("{}Integer", CRATE_SYN_PREFIX, name), + RustType::U8(_) => format!("{}Integer", CRATE_SYN_PREFIX, name), + RustType::I16(_) => format!("{}Integer", CRATE_SYN_PREFIX, name), + RustType::U16(_) => format!("{}Integer", CRATE_SYN_PREFIX, name), + RustType::I32(_) => format!("{}Integer", CRATE_SYN_PREFIX, name), + RustType::U32(_) => format!("{}Integer", CRATE_SYN_PREFIX, name), + RustType::I64(_) => format!("{}Integer", CRATE_SYN_PREFIX, name), + RustType::U64(Some(_)) => { + format!("{}Integer", CRATE_SYN_PREFIX, name) + } + RustType::U64(None) => format!("{}Integer", CRATE_SYN_PREFIX), + RustType::String => format!("{}Utf8String", CRATE_SYN_PREFIX), + RustType::VecU8 => format!("{}OctetString", CRATE_SYN_PREFIX), + RustType::Vec(inner) => format!( + "{}SequenceOf<{}>", + CRATE_SYN_PREFIX, + Self::type_declaration(&*inner, name) + ), + RustType::Option(inner) => format!("Option<{}>", Self::type_declaration(&*inner, name)), + RustType::Complex(inner) => format!("{}Complex<{}>", CRATE_SYN_PREFIX, inner), + } + } + + fn write_type_declaration(&self, scope: &mut Scope, base: &str, name: &str, r#type: &RustType) { + let combined = Self::combined_field_type_name(base, name); + let type_dec = Self::type_declaration(r#type, &combined); + scope.raw(&format!("type AsnDef{} = {};", combined, type_dec)); + } + + #[must_use] + pub fn combined_field_type_name(base: &str, name: &str) -> String { + format!( + "{}{}", + RustCodeGenerator::rust_variant_name(base), + RustCodeGenerator::rust_variant_name(name) + ) + } + + fn write_constraints(&self, scope: &mut Scope, Definition(name, r#type): &Definition) { + match r#type { + Rust::Struct(fields) => { + self.write_sequence_constraint(scope, &name, &fields); + self.write_field_constraints(scope, &name, &fields); + } + Rust::Enum(_) => {} + Rust::DataEnum(_) => {} + Rust::TupleStruct(_) => {} + } + } + + fn write_sequence_constraint( + &self, + scope: &mut Scope, + name: &str, + fields: &[(String, RustType)], + ) { + let mut imp = Impl::new(name); + imp.impl_trait(format!("{}sequence::Constraint", CRATE_SYN_PREFIX)); + + self.write_sequence_constraint_read_fn(&mut imp, name, fields); + self.write_sequence_constraint_write_fn(&mut imp, name, fields); + + scope.raw(&Self::write_sequence_constraint_insert_consts( + name, fields, imp, + )); + } + + fn write_field_constraints( + &self, + scope: &mut Scope, + name: &str, + fields: &[(String, RustType)], + ) { + for (field, r#type) in fields { + match r#type { + RustType::Bool => {} + RustType::I8(range) => Self::write_integer_constraint_type( + scope, + name, + field, + &r#type.to_string(), + range, + ), + RustType::U8(range) => Self::write_integer_constraint_type( + scope, + name, + field, + &r#type.to_string(), + range, + ), + RustType::I16(range) => Self::write_integer_constraint_type( + scope, + name, + field, + &r#type.to_string(), + range, + ), + RustType::U16(range) => Self::write_integer_constraint_type( + scope, + name, + field, + &r#type.to_string(), + range, + ), + RustType::I32(range) => Self::write_integer_constraint_type( + scope, + name, + field, + &r#type.to_string(), + range, + ), + RustType::U32(range) => Self::write_integer_constraint_type( + scope, + name, + field, + &r#type.to_string(), + range, + ), + RustType::I64(range) => Self::write_integer_constraint_type( + scope, + name, + field, + &r#type.to_string(), + range, + ), + RustType::U64(Some(range)) => Self::write_integer_constraint_type( + scope, + name, + field, + &r#type.to_string(), + range, + ), + RustType::U64(_) => {} + RustType::String => {} + RustType::VecU8 => {} + RustType::Vec(_) => {} + RustType::Option(_) => {} + RustType::Complex(_) => {} + } + } + } + + fn write_integer_constraint_type( + scope: &mut Scope, + name: &str, + field: &str, + r#type: &str, + Range(min, max): &Range, + ) { + let combined = Self::combined_field_type_name(name, field) + "Constraint"; + + scope.new_struct(&combined).derive("Default"); + scope.raw(&format!( + "impl {}numbers::Constraint<{}> for {} {{", + CRATE_SYN_PREFIX, r#type, combined + )); + scope.raw(&format!("const MIN: Option<{}> = Some({});", r#type, min)); + scope.raw(&format!("const MAX: Option<{}> = Some({});", r#type, max)); + scope.raw("}"); + } + + fn write_sequence_constraint_insert_consts( + name: &str, + fields: &[(String, RustType)], + imp: Impl, + ) -> String { + let string = Scope::new().push_impl(imp).to_string(); + let mut lines = string.lines().map(ToString::to_string).collect::>(); + lines.insert( + 1, + format!( + " const OPTIONAL_FIELDS: usize = {};", + fields.iter().filter(|f| f.1.is_option()).count() + ), + ); + lines.insert(1, format!("const NAME: &'static str = \"{}\";", name)); + lines.join("\n") + } + + fn write_sequence_constraint_read_fn( + &self, + imp: &mut Impl, + name: &str, + fields: &[(String, RustType)], + ) { + imp.new_fn("read_seq") + .generic(&format!("R: {}Reader", CRATE_SYN_PREFIX)) + .arg("reader", "&mut R") + .ret("Result") + .bound("Self", "Sized") + .push_block({ + let mut block = Block::new("Ok(Self"); + + for (field, _type) in fields { + block.line(format!( + "{}: AsnDef{}::read_value(reader)?,", + field, + Self::combined_field_type_name(name, field) + )); + } + + block.after(")"); + block + }); + } + + fn write_sequence_constraint_write_fn( + &self, + imp: &mut Impl, + name: &str, + fields: &[(String, RustType)], + ) { + let body = imp + .new_fn("write_seq") + .generic(&format!("W: {}Writer", CRATE_SYN_PREFIX)) + .arg_ref_self() + .arg("writer", "&mut W") + .ret("Result<(), W::Error>"); + + for (field, _type) in fields { + body.line(format!( + "AsnDef{}::write_value(writer, &self.{})?;", + Self::combined_field_type_name(name, field), + field, + )); + } + + body.line("Ok(())"); + } + + pub fn stringify(model: &Model) -> String { + let mut scope = Scope::new(); + let myself = Self; + + myself.add_imports(&mut scope); + + for definition in &model.definitions { + myself.impl_supplement(&mut scope, definition); + } + + scope.to_string() + } +} + +impl GeneratorSupplement for AsnDefWalker { + fn add_imports(&self, scope: &mut Scope) { + scope.raw("use asn1rs::prelude::*;"); + } + + fn impl_supplement(&self, scope: &mut Scope, definition: &Definition) { + self.write_type_definitions(scope, definition); + self.write_constraints(scope, definition); + } +} + +#[cfg(test)] +pub mod tests { + use crate::gen::rust::walker::AsnDefWalker; + use crate::model::{Definition, Rust, RustType}; + use codegen::Scope; + + fn simple_whatever_sequence() -> Definition { + Definition( + String::from("Whatever"), + Rust::Struct(vec![ + (String::from("name"), RustType::String), + ( + String::from("opt"), + RustType::Option(Box::new(RustType::String)), + ), + ( + String::from("some"), + RustType::Option(Box::new(RustType::String)), + ), + ]), + ) + } + + #[test] + pub fn test_whatever_struct_type_declaration() { + let def = simple_whatever_sequence(); + let mut scope = Scope::new(); + AsnDefWalker.write_type_definitions(&mut scope, &def); + let string = scope.to_string(); + println!("{}", string); + let mut lines = string.lines().filter(|l| !l.is_empty()); + assert_eq!( + Some("type AsnDefWhatever = ::asn1rs::syn::Sequence;"), + lines.next() + ); + assert_eq!( + Some("type AsnDefWhateverName = ::asn1rs::syn::Utf8String;"), + lines.next() + ); + assert_eq!( + Some("type AsnDefWhateverOpt = Option<::asn1rs::syn::Utf8String>;"), + lines.next() + ); + assert_eq!( + Some("type AsnDefWhateverSome = Option<::asn1rs::syn::Utf8String>;"), + lines.next() + ); + } + + #[test] + pub fn test_whatever_struct_constraint_impl() { + let def = simple_whatever_sequence(); + let mut scope = Scope::new(); + AsnDefWalker.write_constraints(&mut scope, &def); + let string = scope.to_string(); + println!("{}", string); + + fn assert_lines(expected: &str, actual: &str) { + let mut expected = expected.lines().map(|l| l.trim()).filter(|l| !l.is_empty()); + let mut actual = actual.lines().map(|l| l.trim()).filter(|l| !l.is_empty()); + + loop { + let expected = expected.next(); + let actual = actual.next(); + assert_eq!(expected, actual); + if expected.is_none() && actual.is_none() { + break; + } + } + } + + assert_lines( + r#" + impl ::asn1rs::syn::sequence::Constraint for Whatever { + const NAME: &'static str = "Whatever"; + const OPTIONAL_FIELDS: usize = 2; + + fn read_seq(reader: &mut R) -> Result + where Self: Sized, + { + Ok(Self { + name: AsnDefWhateverName::read_value(reader)?, + opt: AsnDefWhateverOpt::read_value(reader)?, + some: AsnDefWhateverSome::read_value(reader)?, + }) + } + + fn write_seq(&self, writer: &mut W) -> Result<(), W::Error> { + AsnDefWhateverName::write_value(writer, &self.name)?; + AsnDefWhateverOpt::write_value(writer, &self.opt)?; + AsnDefWhateverSome::write_value(writer, &self.some)?; + Ok(()) + } + } + "#, + &string, + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 560096e3..1b4acd95 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,3 +18,7 @@ pub use asn1rs_model::parser; pub mod io; pub mod syn; + +pub mod prelude { + pub use crate::syn::*; +} diff --git a/src/syn/mod.rs b/src/syn/mod.rs index a5e66170..1b3a832a 100644 --- a/src/syn/mod.rs +++ b/src/syn/mod.rs @@ -1,9 +1,14 @@ use std::marker::PhantomData; +pub mod numbers; pub mod optional; pub mod sequence; pub mod utf8string; +pub use numbers::Integer; +pub use sequence::Sequence; +pub use utf8string::Utf8String; + pub trait Reader { type Error; @@ -18,6 +23,10 @@ pub trait Reader { fn read_opt(&mut self) -> Result, Self::Error>; + fn read_int(&mut self, range: (i64, i64)) -> Result; + + fn read_int_max(&mut self) -> Result; + fn read_utf8string(&mut self) -> Result; } @@ -53,6 +62,10 @@ pub trait Writer { fn write_opt(&mut self, value: Option<&T::Type>) -> Result<(), Self::Error>; + fn write_int(&mut self, value: i64, range: (i64, i64)) -> Result<(), Self::Error>; + + fn write_int_max(&mut self, value: u64) -> Result<(), Self::Error>; + fn write_utf8string( &mut self, value: &str, @@ -96,7 +109,7 @@ mod tests { impl sequence::Constraint for Whatever { const NAME: &'static str = "Whatever"; - const OPTIONAL_FIELDS: usize = 1; + const OPTIONAL_FIELDS: usize = 2; fn read_seq(reader: &mut R) -> Result::Error> where @@ -163,6 +176,16 @@ mod tests { }) } + fn write_int(&mut self, value: i64, (min, max): (i64, i64)) -> Result<(), Self::Error> { + self.indented_println(&format!("WRITING Integer({}..{}) {}", min, max, value)); + Ok(()) + } + + fn write_int_max(&mut self, value: u64) -> Result<(), Self::Error> { + self.indented_println(&format!("WRITING Integer {}", value)); + Ok(()) + } + fn write_utf8string( &mut self, value: &str, diff --git a/src/syn/numbers.rs b/src/syn/numbers.rs new file mode 100644 index 00000000..1f044af9 --- /dev/null +++ b/src/syn/numbers.rs @@ -0,0 +1,106 @@ +use crate::syn::{ReadableType, Reader, WritableType, Writer}; +use core::marker::PhantomData; +use std::convert::TryFrom; + +#[derive(Default)] +pub struct Integer = NoConstraint>(PhantomData, PhantomData); + +pub trait Constraint { + const MIN: Option = None; + const MAX: Option = None; +} + +#[derive(Default)] +pub struct NoConstraint; + +impl Constraint for NoConstraint {} + +macro_rules! read_write { + ( $($T:ident),+ ) => {$( + + impl> WritableType for Integer<$T, C> { + type Type = $T; + + fn write_value( + writer: &mut W, + value: &Self::Type, + ) -> Result<(), ::Error> { + let value = *value; + if C::MIN.is_none() && C::MAX.is_none() { + writer.write_int_max(value as u64) + } else { + writer.write_int( + i64::from(value), + ( + C::MIN.map(i64::from).unwrap_or(0), + C::MAX.map(i64::from).unwrap_or_else(i64::max_value), + ), + ) + } + } + } + + impl> ReadableType for Integer<$T, C> { + type Type = $T; + + fn read_value(reader: &mut R) -> Result::Error> { + if C::MIN.is_none() && C::MAX.is_none() { + Ok(reader.read_int_max()? as $T) + } else { + Ok(reader + .read_int(( + C::MIN.map(i64::from).unwrap_or(0), + C::MAX.map(i64::from).unwrap_or_else(i64::max_value), + ))? as $T + ) + } + } + } + )* + } +} + +read_write!(i8, i16, i32, i64); +read_write!(u8, u16, u32); + +impl> WritableType for Integer { + type Type = u64; + + fn write_value( + writer: &mut W, + value: &Self::Type, + ) -> Result<(), ::Error> { + let value = *value; + if C::MIN.is_none() & &C::MAX.is_none() { + writer.write_int_max(value) + } else { + let value = i64::try_from(value).unwrap(); + writer.write_int( + value, + ( + C::MIN.map(|v| i64::try_from(v).unwrap()).unwrap_or(0), + C::MAX + .map(|v| i64::try_from(v).unwrap()) + .unwrap_or_else(i64::max_value), + ), + ) + } + } +} + +impl> ReadableType for Integer { + type Type = u64; + + fn read_value(reader: &mut R) -> Result::Error> { + if C::MIN.is_none() && C::MAX.is_none() { + Ok(reader.read_int_max()?) + } else { + Ok(reader.read_int(( + C::MIN.map(|v| i64::try_from(v).unwrap()).unwrap_or(0), + C::MAX + .map(|v| i64::try_from(v).unwrap()) + .unwrap_or_else(i64::max_value), + ))? as u64) + } + } +} diff --git a/tests/basic_proc_macro_attribute.rs b/tests/basic_proc_macro_attribute.rs index af205af8..a592f0b2 100644 --- a/tests/basic_proc_macro_attribute.rs +++ b/tests/basic_proc_macro_attribute.rs @@ -10,7 +10,7 @@ pub struct Potato { #[asn(integer(min..max))] size2: u64, #[asn(integer(12..128), tag(APPLICATION(4)))] - size3: u64, + size3: u8, #[asn(utf8string, tag(4))] string: String, } @@ -20,7 +20,7 @@ fn test_compiles() { let _ = Potato { size: 123, size2: 1234, - size3: 1234, + size3: 234, string: String::from("where is the content"), }; }