#![forbid(unsafe_code)]
use hexhex_impl::decode;
use proc_macro::{Literal, TokenStream, TokenTree};
macro_rules! return_compile_error {
($($fmt:tt)*) => {{
return compile_error(&format!($($fmt)*));
}}
}
#[proc_macro]
pub fn hex_literal(input: TokenStream) -> TokenStream {
let mut iter = input.into_iter();
match iter.next() {
Some(TokenTree::Literal(lit)) => {
let s = lit.to_string();
let content = if s.starts_with('"') && s.ends_with('"') {
&s[1..s.len() - 1]
} else if s.starts_with("b\"") && s.ends_with('"') {
&s[2..s.len() - 1]
} else {
return_compile_error!("unexpected argument {lit}");
};
let bytes = match decode(content) {
Ok(x) => x,
Err(e) => return_compile_error!("{e}"),
};
if let Some(tree) = iter.next() {
return_compile_error!("unexpected argument {tree}");
}
[TokenTree::from(Literal::byte_string(&bytes))]
.into_iter()
.collect()
}
None => return_compile_error!("expected a string or bytestring"),
_ => return_compile_error!("unexpected argument"),
}
}
fn compile_error(msg: &str) -> TokenStream {
use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span};
[
TokenTree::Punct(Punct::new(':', Spacing::Joint)),
TokenTree::Punct(Punct::new(':', Spacing::Joint)),
TokenTree::Ident(Ident::new("std", Span::mixed_site())),
TokenTree::Punct(Punct::new(':', Spacing::Joint)),
TokenTree::Punct(Punct::new(':', Spacing::Joint)),
TokenTree::Ident(Ident::new("compile_error", Span::mixed_site())),
TokenTree::Punct(Punct::new('!', Spacing::Alone)),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
[TokenTree::Literal(Literal::string(msg))]
.into_iter()
.collect(),
)),
]
.into_iter()
.collect()
}