Skip to content

Commit 9ca238a

Browse files
committed
Initial commit, working implementation.
0 parents  commit 9ca238a

File tree

10 files changed

+188
-0
lines changed

10 files changed

+188
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[workspace]
2+
members = ["include-lua", "include-lua-macro", "example" ]

example/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "lua-include-example"
3+
version = "0.1.0"
4+
authors = ["AlphaModder"]
5+
edition = "2018"
6+
publish = false
7+
8+
[dependencies]
9+
include-lua = { path = "../include-lua" }
10+
rlua = "0.16.2"

example/src/lib/alpha/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
return "Hello World!"

example/src/lib/test.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
return "Hello Again!"

example/src/main.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use include_lua::*;
2+
use rlua::Lua;
3+
4+
fn main() -> rlua::Result<()> {
5+
let lua = Lua::new();
6+
let modules = include_lua!("lib");
7+
lua.context(|ctx| -> rlua::Result<()> {
8+
ctx.add_modules(modules)?;
9+
println!("{}", ctx.load("require('alpha')").eval::<String>()?);
10+
println!("{}", ctx.load("require('test')").eval::<String>()?);
11+
Ok(())
12+
})
13+
14+
}

include-lua-macro/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "include-lua-macro"
3+
version = "0.1.0"
4+
authors = ["AlphaModder"]
5+
edition = "2018"
6+
7+
[lib]
8+
proc-macro = true
9+
10+
[dependencies]
11+
syn = "0.15"
12+
quote = "0.6"
13+
walkdir = "2.2"
14+
proc-macro-hack = "0.5.4"

include-lua-macro/src/lib.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
extern crate proc_macro;
2+
3+
use std::{env, path::{self, PathBuf}};
4+
use proc_macro_hack::proc_macro_hack;
5+
use quote::quote;
6+
use syn::{
7+
parse_macro_input, Result, LitStr, Token,
8+
parse::{Parse, ParseStream},
9+
export::{Span, TokenStream2 as TokenStream}
10+
};
11+
use walkdir::WalkDir;
12+
13+
14+
#[proc_macro_hack]
15+
pub fn include_lua(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
16+
parse_macro_input!(input as IncludeLua).expand().into()
17+
}
18+
19+
struct IncludeLua(LitStr, LitStr);
20+
21+
impl 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+
// Do paths with a different separator show up? If so, fix this.
35+
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 path = LitStr::new(&PathBuf::from(self.0.value()).join(path).to_string_lossy(), Span::call_site());
51+
quote! { files.insert(#module.to_string(), include_str!(#path).to_string()) }
52+
});
53+
54+
let name = &self.1;
55+
quote! { {
56+
#[allow(unknown_lints)]
57+
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
58+
#[allow(rust_2018_idioms)]
59+
extern crate include_lua as _include_lua;
60+
61+
let mut files = ::std::collections::HashMap::<String, String>::new();
62+
#(#add_files;)*
63+
_include_lua::LuaModules::__new(files, #name)
64+
} }
65+
}
66+
}
67+
68+
impl Parse for IncludeLua {
69+
fn parse(input: ParseStream) -> Result<Self> {
70+
let (path_str, name) = {
71+
let s1: LitStr = input.parse()?;
72+
(s1.clone(), if let Err(_) = input.parse::<Token![:]>() { s1 } else { input.parse()? })
73+
};
74+
if !input.is_empty() { return Err(input.error("Unknown token in include_lua invocation!")) }
75+
Ok(IncludeLua(path_str, name))
76+
}
77+
}

include-lua/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "include-lua"
3+
version = "0.1.0"
4+
authors = ["AlphaModder"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
rlua = "0.16.2"
9+
include-lua-macro = { path = "../include-lua-macro" }
10+
proc-macro-hack = "0.5.4"

include-lua/src/lib.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use std::collections::HashMap;
2+
3+
use proc_macro_hack::proc_macro_hack;
4+
use rlua::{Result, Context, UserData, UserDataMethods, MetaMethod, Value, Table, RegistryKey};
5+
6+
#[proc_macro_hack]
7+
pub use include_lua_macro::include_lua;
8+
9+
pub struct LuaModules {
10+
files: HashMap<String, String>,
11+
name: String,
12+
}
13+
14+
impl LuaModules {
15+
#[doc(hidden)] // This is not a public API!
16+
pub fn __new(files: HashMap<String, String>, name: &str) -> LuaModules {
17+
LuaModules { files: files, name: name.to_string() }
18+
}
19+
}
20+
21+
struct Searcher(LuaModules, RegistryKey);
22+
23+
impl UserData for Searcher {
24+
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
25+
methods.add_meta_method(MetaMethod::Call, |ctx, this, value: String| {
26+
Ok(match this.0.files.get(&value) {
27+
Some(source) => {
28+
Value::Function(ctx.load(source)
29+
.set_name(&this.0.name)?
30+
.set_environment(ctx.registry_value::<Table>(&this.1)?)?
31+
.into_function()?
32+
)
33+
}
34+
None => Value::Nil,
35+
})
36+
});
37+
}
38+
}
39+
40+
pub trait ContextExt<'a> {
41+
fn add_modules(&self, modules: LuaModules) -> Result<()>;
42+
fn add_modules_with_env(&self, modules: LuaModules, environment: Table<'a>) -> Result<()>;
43+
}
44+
45+
impl<'a> ContextExt<'a> for Context<'a> {
46+
fn add_modules(&self, modules: LuaModules) -> Result<()> {
47+
self.add_modules_with_env(modules, self.globals())
48+
}
49+
50+
fn add_modules_with_env(&self, modules: LuaModules, environment: Table<'a>) -> Result<()> {
51+
let key = self.create_registry_value(environment)?;
52+
let searchers: Table = self.globals().get::<_, Table>("package")?.get("searchers")?;
53+
searchers.set(searchers.len()? + 1, Searcher(modules, key))?;
54+
Ok(())
55+
}
56+
}

0 commit comments

Comments
 (0)