extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, Data, DeriveInput, Fields};
#[proc_macro_derive(Response, attributes(apdu, sw1, sw2, payload, mask))]
pub fn derive_response(input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as DeriveInput);
let output: proc_macro2::TokenStream = match item.data {
Data::Enum(d) => {
let ty = &item.ident;
let gen = &item.generics;
let gen_suffix = match gen.into_token_stream().is_empty() {
true => quote! {},
_ => quote! { ::#gen },
};
let arms = d
.variants
.iter()
.flat_map(|variant| {
variant
.attrs
.iter()
.filter(|attr| attr.path.is_ident("apdu"))
.map(move |attr| (variant, attr))
})
.map(|(variant, attr)| {
let ident = &variant.ident;
let tokens = &attr.tokens;
let left = match tokens.is_empty() {
true => quote! { _ },
_ => tokens.clone(),
};
let fields = match &variant.fields {
Fields::Named(f) => f.named.iter().collect(),
Fields::Unnamed(f) => f.unnamed.iter().collect(),
Fields::Unit => vec![],
};
let right = if fields.iter().any(|f| !f.attrs.is_empty()) {
let values = fields.iter().map(|f| {
let mask = if let Some(attr) =
f.attrs.iter().find(|a| a.path.is_ident("mask"))
{
let m = &attr.tokens;
quote! { & #m }
} else {
quote! {}
};
if f.attrs.iter().any(|a| a.path.is_ident("sw1")) {
quote! { (response.trailer.0 #mask).into(), }
} else if f.attrs.iter().any(|a| a.path.is_ident("sw2")) {
quote! { (response.trailer.1 #mask).into(), }
} else if f.attrs.iter().any(|a| a.path.is_ident("payload")) {
quote! { response.payload.into(), }
} else {
quote! {}
}
});
quote! { #ty #gen_suffix::#ident(#(#values)*) }
} else if variant.fields.is_empty() {
quote! { #ty #gen_suffix::#ident }
} else if variant.fields.len() == 1 {
quote! { #ty #gen_suffix::#ident(response.payload) }
} else if variant.fields.len() == 2 {
quote! { #ty #gen_suffix::#ident(response.trailer.0, response.trailer.1) }
} else {
panic!("unsupported type of fields found")
};
quote! { #left => #right, }
});
quote! {
impl<'a> ::std::convert::From<::apdu_core::Response<'a>> for #ty #gen {
fn from(response: ::apdu_core::Response<'a>) -> Self {
let (sw1, sw2) = response.trailer;
match (sw1, sw2) {
#(#arms)*
}
}
}
impl<'a> ::std::convert::From<::apdu_core::Error<'a>> for #ty #gen {
fn from(error: ::apdu_core::Error<'a>) -> Self {
error.response.into()
}
}
impl<'a> ::std::convert::From<&'a [u8]> for #ty #gen {
fn from(bytes: &'a [u8]) -> Self {
::apdu_core::Response::from(bytes).into()
}
}
}
}
_ => panic!("deriving for Enum is only supported"),
};
proc_macro::TokenStream::from(output)
}