use itertools::Itertools;
use proc_macro::{Delimiter, TokenStream, TokenTree};
#[proc_macro_attribute]
pub fn function_docs(_attr: TokenStream, mut item: TokenStream) -> TokenStream {
item.extend(
format!(
"#[doc(hidden)]pub static ___FUNCTION_DOCS_{}: &str = {:?};",
if let Some(x) =
item.clone()
.into_iter()
.tuple_windows()
.find_map(|(x, y)| match (x, y) {
(TokenTree::Ident(a), TokenTree::Ident(b)) if a.to_string() == "fn" =>
Some(b),
_ => None,
})
{
x
} else {
return "compile_error!(\"function_docs only supports function docs\");"
.parse()
.unwrap();
},
item.clone()
.into_iter()
.tuple_windows()
.filter_map(|(x, y)| match (x, y) {
(TokenTree::Punct(p), TokenTree::Group(g))
if p.as_char() == '#' && g.delimiter() == Delimiter::Bracket =>
{
let mut iter = g.stream().into_iter();
match (iter.next(), iter.next(), iter.next()) {
(
Some(TokenTree::Ident(i)),
Some(TokenTree::Punct(p)),
Some(TokenTree::Literal(s)),
) if i.to_string() == "doc" && p.as_char() == '=' => Some(
s.to_string()
.strip_prefix("\"")
.unwrap()
.strip_suffix("\"")
.unwrap()
.trim_start_matches(' ')
.to_owned(),
),
_ => None,
}
}
_ => None,
})
.collect::<Vec<String>>()
.join("\n"),
)
.parse::<TokenStream>()
.unwrap(),
);
item
}
#[proc_macro]
pub fn get_docs(item: TokenStream) -> TokenStream {
let mut iter = item.into_iter();
match (iter.next(), iter.next()) {
(Some(TokenTree::Ident(i)), None) => {
format!("___FUNCTION_DOCS_{}", i.to_string()).parse().unwrap()
}
_ => "compile_error!(\"expected ident\");".parse().unwrap(),
}
}