#![feature(proc_macro)]
extern crate proc_macro;
use proc_macro::{TokenStream, TokenNode};
use std::str::FromStr;
#[proc_macro]
pub fn hb( tokens : TokenStream ) -> TokenStream {
let mut iter = tokens.clone().into_iter();
let first_token_opt = iter.next();
let first_token = match first_token_opt {
Some(t) => t,
_ => panic!( "Expected string literal" )
};
if iter.next().is_some() {
panic!( "Too many arguments to hb! macro" );
}
let literal = match first_token.kind {
TokenNode::Literal( l ) => l,
_ => panic!( "Expected string literal" ),
};
let value = literal.to_string();
let mut value_iter = value.chars();
match value_iter.next() {
Some( c ) if c != '"' => panic!( "Expected string literal" ),
None => panic!( "Expected string literal" ),
_ => {}
}
#[derive(PartialEq)]
enum State { First, Second };
let mut state = State::First;
let mut first_digit = 0;
let mut bytes = vec![];
let mut final_quote = false;
while let Some( c ) = value_iter.next() {
if c == '"' {
final_quote = true;
break;
}
state = match state {
State::First => match c_to_hex( c ) {
Some(v) => {
first_digit = v;
State::Second
}
None => State::First
},
State::Second => match c_to_hex( c ) {
Some(v) => {
bytes.push( ( first_digit << 4 ) + v );
State::First
}
None => panic!( "Bytes must consist of pairs of hex-digits" ),
},
};
}
if let Some(c) = value_iter.next() {
panic!( "Trailing characters after the string literal: {}", c );
}
if ! final_quote {
panic!( "String literal must end in a final quote" );
}
if state == State::Second {
panic!( "Last byte was incomplete." );
}
let literals : Vec<_> = bytes.into_iter()
.map( |v| format!( "{}u8", v ) )
.collect();
let output_str = format!( "[{}]", literals.join( ", " ) );
TokenStream::from_str( &output_str ).unwrap()
}
fn c_to_hex( c : char ) -> Option< u8 > {
Some( match c {
'0' => 0,
'1' => 1,
'2' => 2,
'3' => 3,
'4' => 4,
'5' => 5,
'6' => 6,
'7' => 7,
'8' => 8,
'9' => 9,
'a' | 'A' => 10,
'b' | 'B' => 11,
'c' | 'C' => 12,
'd' | 'D' => 13,
'e' | 'E' => 14,
'f' | 'F' => 15,
n if n.is_whitespace() => return None,
_ => panic!( "Invalid character in hex string: {}", c ),
} )
}