1extern crate proc_macro;
2
3use std::{env, path::{self, PathBuf}};
4use proc_macro_hack::proc_macro_hack;
5use quote::quote;
6use syn::{
7 parse_macro_input, Result, LitStr, Token,
8 parse::{Parse, ParseStream},
9 export::{Span, TokenStream2 as TokenStream}
10};
11use walkdir::WalkDir;
12
13
14#[proc_macro_hack]
15pub fn include_lua(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
16 parse_macro_input!(input as IncludeLua).expand().into()
17}
18
19struct IncludeLua(LitStr, LitStr);
20
21impl IncludeLua {
22 fn expand(self) -> TokenStream {
23 let manifest_dir: PathBuf = env::var("CARGO_MANIFEST_DIR").expect("Could not locate active Cargo.toml!").into();
24 let lua_dir = manifest_dir.join("src").join(self.0.value());
25 let modules = WalkDir::new(&lua_dir).into_iter().filter_map(|entry| {
26 match entry {
27 Ok(ref entry) if entry.file_type().is_file() => {
28 let path = entry.path().strip_prefix(&lua_dir).expect("Reached file outside of lua directory???");
29 if path.extension() == Some("lua".as_ref()) {
30 let module = if path.parent().is_some() && path.file_stem().expect("Missing file name!") == &"init".as_ref() {
31 path.parent().unwrap().to_str().map(|s| s.replace(path::MAIN_SEPARATOR, "."))
32 }
33 else {
34 let mut s = path.to_str().map(|s| s.replace(path::MAIN_SEPARATOR, "."));
36 s.as_mut().map(|s| s.truncate(s.len() - 4));
37 s
38 };
39 return module.map(|module| (module, path.to_owned()))
40 }
41 None
42 }
43 Err(e) => panic!("An error occured while searching for lua modules: {}", e),
44 _ => None,
45 }
46 });
47
48 let add_files = modules.map(|(module, path)| {
49 let module = LitStr::new(&module, Span::call_site());
50 let real_path = LitStr::new(&PathBuf::from(self.0.value()).join(&path).to_string_lossy(), Span::call_site());
51 let virtual_path = LitStr::new(&path.to_string_lossy(), Span::call_site());
52 quote! {
53 files.insert(#module.to_string(), (include_str!(#real_path).to_string(), #virtual_path.to_string()))
54 }
55 });
56
57 let name = &self.1;
58 quote! { {
59 #[allow(unknown_lints)]
60 #[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
61 #[allow(rust_2018_idioms)]
62 extern crate include_lua as _include_lua;
63
64 let mut files = ::std::collections::HashMap::<String, (String, String)>::new();
65 #(#add_files;)*
66 _include_lua::LuaModules::__new(files, #name)
67 } }
68 }
69}
70
71impl Parse for IncludeLua {
72 fn parse(input: ParseStream) -> Result<Self> {
73 let (path_str, name) = {
74 let s1: LitStr = input.parse()?;
75 match input.parse::<Token![:]>() {
76 Ok(_) => (input.parse()?, s1),
77 Err(_) => (s1.clone(), s1),
78 }
79 };
80 if !input.is_empty() { return Err(input.error("Unknown token in include_lua invocation!")) }
81 Ok(IncludeLua(path_str, name))
82 }
83}