use std::iter::FromIterator;
use std::{fs, path::Path};
use quote::quote;
use syn::{parse_macro_input, Lit};
use usdt_impl::compile_provider_source;
#[proc_macro]
pub fn dtrace_provider(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut tokens = item.into_iter().collect::<Vec<proc_macro::TokenTree>>();
let comma_index = tokens
.iter()
.enumerate()
.find_map(|(i, token)| match token {
proc_macro::TokenTree::Punct(p) if p.as_char() == ',' => Some(i),
_ => None,
});
let rest = if let Some(index) = comma_index {
let mut rest = tokens.split_off(index);
let _ = rest.remove(0);
rest
} else {
Vec::new()
};
let config: usdt_impl::CompileProvidersConfig = serde_tokenstream::from_tokenstream(
&proc_macro2::TokenStream::from(proc_macro::TokenStream::from_iter(rest)),
)
.unwrap();
let first_item = proc_macro::TokenStream::from_iter(tokens);
let tok = parse_macro_input!(first_item as Lit);
let filename = match tok {
Lit::Str(f) => f.value(),
_ => panic!("DTrace provider must be a single literal string filename"),
};
let source = if filename.ends_with(".d") {
let dir = std::env::var("CARGO_MANIFEST_DIR").map_or_else(
|_| std::env::current_dir().unwrap(),
|s| Path::new(&s).to_path_buf(),
);
let path = dir.join(&filename);
fs::read_to_string(path).unwrap_or_else(|_| {
panic!(
"Could not read D source file \"{}\" in {:?}",
&filename, dir,
)
})
} else {
filename.clone()
};
match compile_provider_source(&source, &config) {
Ok(provider) => provider.into(),
Err(e) => {
let message = format!(
"Error building provider definition in \"{}\"\n\n{}",
filename, e
);
let out = quote! {
compile_error!(#message);
};
out.into()
}
}
}