extern crate proc_macro; use proc_macro::{Literal, TokenStream, TokenTree}; use proc_macro_hack::proc_macro_hack; use proc_macro_roids::{DeriveInputExt, DeriveInputStructExt, FieldExt}; use quote::{format_ident, quote}; use std::str::FromStr; use byteorder::{BigEndian, LittleEndian, NativeEndian}; use proc_macro2::TokenStream as TokenStream2; use std::iter::FromIterator; use std::mem::align_of; use syn::parse::{Parse, ParseBuffer, Parser}; use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Expr, LitStr, Token}; #[derive(Debug)] struct UnpackInput { pattern: String, input: Box, } impl Parse for UnpackInput { fn parse<'a>(input: &'a ParseBuffer<'a>) -> Result { let pattern = input.parse::()?.value().into(); input.parse::()?; Ok(UnpackInput { pattern, input: input.parse()?, }) } } enum Endian { NativeEndian, LittleEndian, BigEndian, } #[proc_macro_hack] pub fn unpack(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as UnpackInput); if input.pattern.is_empty() { return quote! { () }.into(); } let (order, alignment, indic) = match input .pattern .chars() .nth(0) .expect("Can't read first letter of pattern") { '!' | '>' => (Endian::BigEndian, false, true), '<' => (Endian::LittleEndian, false, true), '=' => (Endian::NativeEndian, false, true), '@' => (Endian::NativeEndian, true, true), _ => (Endian::NativeEndian, true, false), }; let rest = if indic { input.pattern.chars().skip(1).collect() } else { input.pattern }; let mut i = 0; let x: &[u8] = &[]; let endian = match order { Endian::BigEndian => quote! { ::packd::parser::Endian::BigEndian }, Endian::LittleEndian => quote! { ::packd::parser::Endian::LittleEndian }, Endian::NativeEndian => quote! { ::packd::parser::Endian::NativeEndian }, }; let mut parsers: Vec<_> = vec![]; let mut marked_values = vec![]; let mut offset = 0; for char in rest.chars() { let value = format_ident!("value_{}", i.to_string()); match char { 'x' => offset += 1, 'h' => { marked_values.push(i); parsers.push(quote! { let #value = ::packd::parser::unpack_h(#endian, &input[offset..offset + 2]); offset += 2; }); } 'H' => { marked_values.push(i); parsers.push(quote! { let #value = ::packd::parser::unpack_H(#endian, &input[offset..offset + 2]); offset += 2; }); if alignment { parsers.push(quote! { offset += 2 % ::std::mem::align_of::(); }); } } 'l' => { marked_values.push(i); parsers.push(quote! { let #value = ::packd::parser::unpack_l(#endian, &input[offset..offset + 4]); offset += 4; }); if alignment { parsers.push(quote! { offset += 2 % ::std::mem::align_of::(); }); } } _ => panic!("Found unrecognized format char '{}'", char), } i += 1; } let items = marked_values .iter() .map(|x| format_ident!("value_{}", x.to_string())) .collect::>(); let input_val = input.input; return quote! { { let input = #input_val; let mut offset = 0; #(#parsers)* (#(#items,)*) } } .into(); }