Skip to content

Commit

Permalink
Support transparent ASN types - where there is only one unnamed field #…
Browse files Browse the repository at this point in the history
  • Loading branch information
kellerkindt committed Apr 30, 2020
1 parent 075e5d9 commit bad87a0
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 7 deletions.
84 changes: 79 additions & 5 deletions asn1rs-macros/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use asn1rs_model::model::{
use proc_macro::TokenStream;
use quote::quote;
use range::MaybeRanged;
use std::convert::Infallible;
use std::str::FromStr;
use syn::export::TokenStream2;
use syn::parenthesized;
Expand Down Expand Up @@ -48,8 +49,11 @@ pub(crate) fn parse(attr: TokenStream, item: TokenStream) -> TokenStream {
Item::Struct(mut strct) if asn_type_decl == "sequence" => {
let mut fields = Vec::new();
for field in strct.fields.iter_mut() {
if field.ident.is_none() {
return compile_error_ts(field.span(), "Unnamed fields are not allowed here");
}
let mut removed = None;
'inner: for i in 0..field.attrs.len() {
for i in 0..field.attrs.len() {
if field.attrs[i]
.path
.segments
Expand All @@ -60,7 +64,6 @@ pub(crate) fn parse(attr: TokenStream, item: TokenStream) -> TokenStream {
.eq("asn")
{
removed = Some(field.attrs.remove(i));
break 'inner;
}
}
if let Some(removed) = removed {
Expand Down Expand Up @@ -94,6 +97,59 @@ pub(crate) fn parse(attr: TokenStream, item: TokenStream) -> TokenStream {

Item::Struct(strct)
}
Item::Struct(mut strct) if asn_type_decl == "transparent" => {
if strct.fields.len() != 1 || strct.fields.iter().next().unwrap().ident.is_some() {
return compile_error_ts(
strct.span(),
"Transparent structs have to have exactly one unnamed field",
);
}

let field = strct.fields.iter_mut().next().unwrap();
let mut attribute = None;
'inner: for i in 0..field.attrs.len() {
if field.attrs[i]
.path
.segments
.first()
.unwrap()
.ident
.to_string()
.eq("asn")
{
attribute = Some(field.attrs.remove(i));
break 'inner;
}
}

let r#type = if let Some(attribute) = attribute {
match attribute.parse_args::<Asn>() {
Ok(asn) => match into_asn(&field.ty, asn) {
Some(asn) => asn,
None => {
return compile_error_ts(attribute.span(), "Missing ASN-Type");
}
},
Err(e) => return TokenStream::from(e.to_compile_error()),
}
} else {
return compile_error_ts(
field.span(),
"Field has is missing a [asn(...)] attribute",
);
};

println!("---------- parsed");
let definition = Definition(strct.ident.to_string(), r#type);
println!("{:#?}", definition);
model.definitions.push(definition);

println!("---------- output");
let st = Item::Struct(strct.clone());
println!("{}", TokenStream::from(quote! {#st}).to_string());

Item::Struct(strct)
}
Item::Enum(enm) if asn_type_decl == "enumerated" => {
let plain_enum = enm.variants.iter().all(|v| v.fields.is_empty());
let variants = enm
Expand Down Expand Up @@ -122,8 +178,11 @@ pub(crate) fn parse(attr: TokenStream, item: TokenStream) -> TokenStream {
.variants
.iter_mut()
.map(|v| {
if v.fields.len() != 1 {
panic!("Variants of CHOICE have to have exactly one field");
if v.fields.len() != 1 || v.fields.iter().next().unwrap().ident.is_some() {
compile_err_ts(
v.span(),
"Variants of CHOICE have to have exactly one unnamed field",
)?;
}
let mut attr = None;
'inner: for i in 0..v.attrs.len() {
Expand Down Expand Up @@ -243,7 +302,7 @@ impl Parse for Asn {
asn.tag = Some(tag.0);
}
attribute => {
return Err(input.error(format!("Unexpected attribute: `{}`", attribute)))
return Err(input.error(format!("Unexpected attribute: `{}`", attribute)));
}
}
}
Expand Down Expand Up @@ -285,3 +344,18 @@ fn parse_type<'a>(input: &'a ParseBuffer<'a>) -> syn::Result<Type> {
r#type => Err(input.error(format!("Unexpected attribute: `{}`", r#type))),
}
}

fn compile_err_ts<T: std::fmt::Display>(
span: proc_macro2::Span,
msg: T,
) -> Result<Infallible, TokenStream> {
Err(compile_error_ts(span, msg))
}

fn compile_error_ts<T: std::fmt::Display>(span: proc_macro2::Span, msg: T) -> TokenStream {
TokenStream::from(compile_error_ts2(span, msg))
}

fn compile_error_ts2<T: std::fmt::Display>(span: proc_macro2::Span, msg: T) -> TokenStream2 {
syn::Error::new(span, msg).to_compile_error()
}
14 changes: 12 additions & 2 deletions asn1rs-model/src/gen/rust/walker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ impl AsnDefWalker {
self.write_type_declaration(scope, &name, &field, r#type);
}
}
Rust::TupleStruct(_) => unimplemented!("TupleStruct in Walker::write_type_definitions"),
Rust::TupleStruct(field) => {
scope.raw(&format!(
"type AsnDef{} = {}Sequence<{}>;",
name, CRATE_SYN_PREFIX, name
));
self.write_type_declaration(scope, &name, "0", field);
}
}
}

Expand Down Expand Up @@ -100,7 +106,11 @@ impl AsnDefWalker {
self.write_enumerated_constraint(scope, &name, plain);
}
Rust::DataEnum(data) => self.write_choice_constraint(scope, &name, data),
Rust::TupleStruct(_) => unimplemented!("TupleStruct for Walker::write_constraints"),
Rust::TupleStruct(field) => {
let fields = [(String::from("0"), field.clone())];
self.write_field_constraints(scope, &name, &fields[..]);
self.write_sequence_constraint(scope, &name, &fields[..]);
}
}
}

Expand Down
90 changes: 90 additions & 0 deletions tests/basic_proc_macro_attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,93 @@ fn test_crazy_list_uper() {
assert_eq!(list, uper.read::<CrazyList>().unwrap());
assert_eq!(0, uper.bits_remaining());
}

#[asn(transparent)]
#[derive(Debug, PartialOrd, PartialEq)]
pub struct FlatList(#[asn(sequence_of(integer))] Vec<u64>);

#[test]
fn test_flat_list_println() {
// Writing sequence FlatList
// Writing sequence-of (MIN..MAX)
// WRITING Integer 13
// WRITING Integer 37
// WRITING Integer 42
PrintlnWriter::default()
.write(&FlatList(vec![13, 37, 42]))
.unwrap();
}

#[test]
fn test_flat_list_uper() {
let mut uper = UperWriter::default();
let v = FlatList(vec![13, 37, 42]);
uper.write(&v).unwrap();
// https://asn1.io/asn1playground/
assert_eq!(
&[0x03, 0x01, 0x0D, 0x01, 0x25, 0x01, 0x2A],
uper.byte_content()
);
assert_eq!(7 * 8, uper.bit_len());
let mut uper = uper.into_reader();
assert_eq!(v, uper.read::<FlatList>().unwrap());
assert_eq!(0, uper.bits_remaining());
}

#[asn(transparent)]
#[derive(Debug, PartialOrd, PartialEq)]
pub struct Important(#[asn(optional(integer))] Option<u64>);

#[test]
fn test_transparent_important_println() {
// Writing sequence FlatList
// Writing sequence-of (MIN..MAX)
// WRITING Integer 13
// WRITING Integer 37
// WRITING Integer 42
PrintlnWriter::default()
.write(&Important(Some(42)))
.unwrap();
}

#[test]
fn test_transparent_important_uper_some() {
let mut uper = UperWriter::default();
let v = Important(Some(42));
uper.write(&v).unwrap();
// invalid according to https://asn1.io/asn1playground/
// but who cares... :P
assert_eq!(
&[
// --- 0
0b1 << 7 // Some
| 0x01 >> 1, // length of the integer, part 1
// --- 1
0x01 << 7 // length of the integer, part 2
| 42 >> 1, // value of the integer, part 1
// --- 2
42 << 7 // value of the integer, part 2
],
uper.byte_content()
);

assert_eq!(2 * 8 + 1, uper.bit_len());
let mut uper = uper.into_reader();
assert_eq!(v, uper.read::<Important>().unwrap());
assert_eq!(0, uper.bits_remaining());
}

#[test]
fn test_transparent_important_uper_none() {
let mut uper = UperWriter::default();
let v = Important(None);
uper.write(&v).unwrap();
// invalid according to https://asn1.io/asn1playground/
// but who cares... :P
assert_eq!(&[0b0 << 7], uper.byte_content());

assert_eq!(1, uper.bit_len());
let mut uper = uper.into_reader();
assert_eq!(v, uper.read::<Important>().unwrap());
assert_eq!(0, uper.bits_remaining());
}

0 comments on commit bad87a0

Please sign in to comment.