Skip to content

Commit

Permalink
Add ToDerSequence custom derive
Browse files Browse the repository at this point in the history
  • Loading branch information
chifflier committed Jan 13, 2025
1 parent 7408ab9 commit 0acc389
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 0 deletions.
55 changes: 55 additions & 0 deletions derive/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,61 @@ impl Container {
}
}
}

pub fn gen_to_der_len(&self) -> TokenStream {
let field_names = &self.fields.iter().map(|f| &f.name).collect::<Vec<_>>();
let add_len_instructions = field_names.iter().fold(Vec::new(), |mut instrs, field| {
instrs.push(quote! {total_len += self.#field.to_der_len()?;});
instrs
});
quote! {
fn to_der_len(&self) -> asn1_rs::Result<usize> {
let mut total_len = 0;
#(#add_len_instructions)*
// now add header length computation
if total_len < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + total_len)
} else {
// 1 (class+tag) + n (length) + len
let n = asn1_rs::Length::Definite(total_len).to_der_len()?;
Ok(1 + n + total_len)
}
}
}
}

pub fn gen_write_der_header(&self) -> TokenStream {
quote! {
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> asn1_rs::SerializeResult<usize> {
let mut empty = std::io::empty();
let num_bytes = self.write_der_content(&mut empty)?;
let header = asn1_rs::Header::new(
asn1_rs::Class::Universal,
true,
asn1_rs::Sequence::TAG,
asn1_rs::Length::Definite(num_bytes),
);
header.write_der_header(writer).map_err(Into::into)
}
}
}

pub fn gen_write_der_content(&self) -> TokenStream {
let field_names = &self.fields.iter().map(|f| &f.name).collect::<Vec<_>>();
let write_instructions = field_names.iter().fold(Vec::new(), |mut instrs, field| {
instrs.push(quote! {num_bytes += self.#field.write_der_header(writer)?;});
instrs.push(quote! {num_bytes += self.#field.write_der_content(writer)?;});
instrs
});
quote! {
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> asn1_rs::SerializeResult<usize> {
let mut num_bytes = 0;
#(#write_instructions)*
Ok(num_bytes)
}
}
}
}

#[derive(Debug)]
Expand Down
4 changes: 4 additions & 0 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ synstructure::decl_derive!([DerSet, attributes(
synstructure::decl_derive!([ToStatic, attributes(
debug_derive
)] => derive_tostatic);

synstructure::decl_derive!([ToDerSequence, attributes(
debug_derive,
)] => derive_toder_sequence);
39 changes: 39 additions & 0 deletions derive/src/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,42 @@ pub fn derive_der_sequence(s: synstructure::Structure) -> proc_macro2::TokenStre
}
ts
}

pub fn derive_toder_sequence(s: synstructure::Structure) -> proc_macro2::TokenStream {
let ast = s.ast();

let container = match &ast.data {
Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Sequence),
_ => panic!("Unsupported type, cannot derive"),
};

let debug_derive = ast.attrs.iter().any(|attr| {
attr.meta
.path()
.is_ident(&Ident::new("debug_derive", Span::call_site()))
});

//let lifetime = Lifetime::new("'ber", Span::call_site());
let wh = &container.where_predicates;

let impl_to_der_len = container.gen_to_der_len();
let impl_write_der_header = container.gen_write_der_header();
let impl_write_der_content = container.gen_write_der_content();

// note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing
// the `where` statement if there are none.
let ts = s.gen_impl(quote! {
extern crate asn1_rs;

#[cfg(feature = "std")]
gen impl asn1_rs::ToDer for @Self where #(#wh)+* {
#impl_to_der_len
#impl_write_der_header
#impl_write_der_content
}
});
if debug_derive {
eprintln!("{}", ts);
}
ts
}
2 changes: 2 additions & 0 deletions src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,5 @@ pub use asn1_rs_derive::DerAlias;
/// struct S<'a>(pub Cow<'a, str>);
/// ```
pub use asn1_rs_derive::ToStatic;

pub use asn1_rs_derive::ToDerSequence;
20 changes: 20 additions & 0 deletions tests/run-pass/toder_sequence_simple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use asn1_rs::*;
use hex_literal::hex;

#[derive(Debug, PartialEq, DerSequence, ToDerSequence)]
#[debug_derive]
pub struct T1 {
a: u32,
b: u16,
c: u16,
}

fn main() {
let input = &hex!("30090201 01020102 020103");
let (rem, t1) = T1::from_der(input).expect("parsing failed");
assert!(rem.is_empty());
assert_eq!(t1, T1 { a: 1, b: 2, c: 3 });
// serialize back data
let output = t1.to_der_vec().expect("serialization failed");
assert_eq!(&input[..], output);
}

0 comments on commit 0acc389

Please sign in to comment.