use proc_macro::TokenStream;
use std::path::PathBuf;
use walkdir::WalkDir;
fn proc_macro_caller_crate_root() -> PathBuf {
let crate_name =
std::env::var("CARGO_PKG_NAME").expect("failed to read ENV var `CARGO_PKG_NAME`!");
let current_dir = std::env::current_dir().expect("failed to unwrap env::current_dir()!");
let search_entry = format!("name=\"{crate_name}\"");
for entry in WalkDir::new(¤t_dir)
.into_iter()
.filter_entry(|e| !e.file_name().eq_ignore_ascii_case("target"))
{
let Ok(entry) = entry else { continue };
if !entry.file_type().is_file() {
continue;
}
let Some(file_name) = entry.path().file_name() else { continue };
if !file_name.eq_ignore_ascii_case("Cargo.toml") {
continue;
}
let Ok(cargo_toml) = std::fs::read_to_string(&entry.path()) else {
continue
};
if cargo_toml
.chars()
.filter(|&c| !c.is_whitespace())
.collect::<String>()
.contains(search_entry.as_str())
{
return entry.path().parent().unwrap().to_path_buf();
}
}
current_dir
}
#[proc_macro]
pub fn my_macro(_tokens: TokenStream) -> TokenStream {
let crate_name = std::env::var("CARGO_PKG_NAME").unwrap();
let working_dir = std::env::current_dir().unwrap().display().to_string();
let root = proc_macro_caller_crate_root()
.as_path()
.to_str()
.unwrap()
.to_string();
println!("CRATE_NAME: {}", crate_name);
println!("WORKING_DIR: {}", working_dir);
println!("CRATE_ROOT: {}", root);
format!("(\"{crate_name}\", \"{working_dir}\", \"{root}\")")
.parse()
.unwrap()
}