#![doc = include_str!("../README.md")]
mod parser;
use std::path::PathBuf;
use anyhow::Context;
use proc_macro::TokenStream;
use syn::{parse::Parse, parse_macro_input};
struct ItemFn {
fn_name: syn::Ident,
_comma: Option<syn::Token![,]>,
relative_path: Option<syn::LitStr>,
}
#[proc_macro]
pub fn inline_fn(input: TokenStream) -> TokenStream {
let input: ItemFn = parse_macro_input!(input);
let relative_path = input
.relative_path
.map(|p| PathBuf::from(p.token().to_string().trim_matches('"')));
let fn_name = input.fn_name.to_string();
let workspace_root = std::env::current_dir().expect("Failed to get current directory");
find_function(fn_name, workspace_root, relative_path)
.parse()
.expect("parse")
}
fn find_function(
fn_name: String,
workspace_root: PathBuf,
relative_path: Option<PathBuf>,
) -> String {
if let Some(source_file) = relative_path {
match find_in_file(fn_name.clone(), workspace_root.join(source_file.clone())) {
Ok(Some(c)) => return c.join("\n"),
Ok(None) => panic!(
"Function `{}` not found, location searched: `{}`",
fn_name,
source_file.display()
),
Err(e) => panic!("{}, location searched: {}", e, source_file.display()),
}
}
walkdir::WalkDir::new(&workspace_root)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.filter(|e| e.path().extension().map(|e| e == "rs").unwrap_or(false))
.map(|e| find_in_file(fn_name.clone(), e.path().to_path_buf()))
.filter_map(|e| e.ok())
.filter(|e| e.is_some())
.map(|e| {
e.with_context(|| {
format!(
"Failed to find function `{}` in folder: {}",
fn_name,
workspace_root.display()
)
})
.unwrap()
})
.flatten()
.collect::<Vec<String>>()
.join("\n")
}
fn find_in_file(fn_name: String, file: PathBuf) -> anyhow::Result<Option<Vec<String>>> {
if file.extension().map(|e| e != "rs").unwrap_or(false) {
return Err(anyhow::anyhow!("File is not a rust file"));
}
let sc = std::fs::read_to_string(file.clone())
.with_context(|| format!("Failed to read file XX: {}", file.display()))?;
Ok(parser::extract_function(&sc, &fn_name).map(|c| {
c.split('\n')
.map(|s| s.to_string())
.collect::<Vec<String>>()
}))
}
impl Parse for ItemFn {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
Ok(Self {
fn_name: input.parse()?,
_comma: input.parse()?,
relative_path: input.parse()?,
})
}
}