diff --git a/crates/lb/src/chan.rs b/crates/lb/src/chan.rs index 52ed7e0..95b0875 100644 --- a/crates/lb/src/chan.rs +++ b/crates/lb/src/chan.rs @@ -1,7 +1,7 @@ // use flume::{Receiver, Sender}; use luaffi::{cdef, metatype}; -#[cdef] +#[cdef(module = "lb:chan")] pub struct lb_chanlib; #[metatype] diff --git a/crates/lb/src/fs.rs b/crates/lb/src/fs.rs index fd3c0e6..e40aa5c 100644 --- a/crates/lb/src/fs.rs +++ b/crates/lb/src/fs.rs @@ -15,7 +15,7 @@ use tokio::fs; /// ```lua /// local fs = require("lb:fs"); /// ``` -#[cdef] +#[cdef(module = "lb:fs")] pub struct lb_fslib; #[metatype] diff --git a/crates/lb/src/net.rs b/crates/lb/src/net.rs index 23f500c..e968324 100644 --- a/crates/lb/src/net.rs +++ b/crates/lb/src/net.rs @@ -37,7 +37,7 @@ type Result = std::result::Result; /// ```lua /// local net = require("lb:net"); /// ``` -#[cdef] +#[cdef(module = "lb:net")] pub struct lb_netlib; #[metatype] diff --git a/crates/lb/src/runtime.rs b/crates/lb/src/runtime.rs index 15f03c9..2b7f4ac 100644 --- a/crates/lb/src/runtime.rs +++ b/crates/lb/src/runtime.rs @@ -1,7 +1,6 @@ use derive_more::{Deref, DerefMut}; -use luaffi::{Registry, Type}; +use luaffi::{Module, Registry}; use luajit::{Chunk, State}; -use std::fmt::Display; use tokio::{ task::{JoinHandle, LocalSet, futures::TaskLocalFuture, spawn_local}, task_local, @@ -23,8 +22,8 @@ impl Builder { &self.registry } - pub fn module(&mut self, name: impl Display) -> &mut Self { - self.registry.preload::(name); + pub fn module(&mut self) -> &mut Self { + self.registry.preload::(); self } diff --git a/crates/lb/src/task.rs b/crates/lb/src/task.rs index a27a3b4..022d493 100644 --- a/crates/lb/src/task.rs +++ b/crates/lb/src/task.rs @@ -3,7 +3,7 @@ use luaffi::{cdef, metatype}; use std::{ffi::c_int, process}; use tokio::task::JoinHandle; -#[cdef] +#[cdef(module = "lb:task")] pub struct lb_tasklib; #[metatype] diff --git a/crates/luaffi/src/lib.rs b/crates/luaffi/src/lib.rs index 31f7657..8ff8ec0 100644 --- a/crates/luaffi/src/lib.rs +++ b/crates/luaffi/src/lib.rs @@ -158,16 +158,16 @@ impl Registry { self } - pub fn preload(&mut self, name: impl Display) -> &mut Self { + pub fn preload(&mut self) -> &mut Self { assert!(T::ty() != TypeType::Void, "cannot declare void type"); - self.include::(); - let ct = T::name(); + let name = ::name(); + let ct = ::name(); writeln!( self.lua, r#"__preload["{name}"] = function(...) return __ct.{ct}(...); end;"#, ) .unwrap(); - self + self.include::() } pub fn build(&self) -> String { @@ -240,6 +240,10 @@ impl<'r> TypeBuilder<'r> { } } +pub trait Module: Type { + fn name() -> impl Display; +} + pub unsafe trait Cdef: Type { fn build(b: &mut CdefBuilder); } @@ -320,6 +324,7 @@ pub struct MetatypeBuilder<'r> { ct: String, cdef: String, lua: String, + lua_includes: Vec<&'static str>, } impl<'r> MetatypeBuilder<'r> { @@ -329,6 +334,7 @@ impl<'r> MetatypeBuilder<'r> { ct: T::Target::name().to_string(), cdef: String::new(), lua: r#"do local __mt, __idx = {}, {}; __mt.__index = __idx; "#.into(), + lua_includes: vec![], } } @@ -337,6 +343,11 @@ impl<'r> MetatypeBuilder<'r> { self } + pub fn include_lua(&mut self, lua: &'static str) -> &mut Self { + self.lua_includes.push(lua); + self + } + pub fn index( &mut self, name: impl Display, @@ -377,12 +388,15 @@ impl<'r> Drop for MetatypeBuilder<'r> { ct, cdef, lua, - .. + lua_includes: lua_postlude, } = self; registry.cdef.push_str(cdef); registry.lua.push_str(lua); writeln!(registry.lua, r#"__metatype(__ct.{ct}, __mt); end;"#).unwrap(); + for lua in lua_postlude { + writeln!(registry.lua, r#"do {lua} end;"#).unwrap(); + } } } diff --git a/crates/luaffi_impl/src/cdef.rs b/crates/luaffi_impl/src/cdef.rs index 4d36022..53e0a3a 100644 --- a/crates/luaffi_impl/src/cdef.rs +++ b/crates/luaffi_impl/src/cdef.rs @@ -5,18 +5,26 @@ use quote::{format_ident, quote, quote_spanned}; use syn::{ext::IdentExt, spanned::Spanned, *}; #[derive(Debug, FromMeta)] -pub struct Args {} +pub struct Args { + module: Option, +} -pub fn transform(_args: Args, mut item: Item) -> Result { - let (name, impl_type, impl_cdef) = match item { +pub fn transform(args: Args, mut item: Item) -> Result { + let (name, impl_type, impl_module, impl_cdef) = match item { Item::Struct(ref mut str) => ( str.ident.clone(), generate_type(&str.ident)?, + args.module + .map(|name| generate_module(&name, &str.ident)) + .transpose()?, generate_cdef_structure(str)?, ), Item::Enum(ref mut enu) => ( enu.ident.clone(), generate_type(&enu.ident)?, + args.module + .map(|name| generate_module(&name, &enu.ident)) + .transpose()?, generate_cdef_enum(enu)?, ), _ => syn_error!(item, "expected struct or enum"), @@ -35,6 +43,7 @@ pub fn transform(_args: Args, mut item: Item) -> Result { mod #mod_name { use super::*; #impl_type + #impl_module #impl_cdef } )) @@ -47,9 +56,7 @@ fn generate_type(ty: &Ident) -> Result { Ok(quote_spanned!(span => unsafe impl #ffi::Type for #ty { - fn name() -> impl ::std::fmt::Display { - #name - } + fn name() -> impl ::std::fmt::Display { #name } fn ty() -> #ffi::TypeType { #ffi::TypeType::Aggregate @@ -72,6 +79,16 @@ fn generate_type(ty: &Ident) -> Result { )) } +fn generate_module(name: &str, ty: &Ident) -> Result { + let ffi = ffi_crate(); + + Ok(quote_spanned!(ty.span() => + impl #ffi::Module for #ty { + fn name() -> impl ::std::fmt::Display { #name } + } + )) +} + fn generate_cdef_structure(str: &mut ItemStruct) -> Result { syn_assert!( str.generics.params.is_empty(), @@ -155,12 +172,11 @@ fn parse_cfield_attrs(attrs: &mut Vec) -> Result { let mut parsed = CFieldAttrs::default(); let mut i = 0; while let Some(attr) = attrs.get(i) { - if let Some(name) = attr.path().get_ident() { - if name == "opaque" { - parsed.opaque = true; - attrs.remove(i); - continue; - } + let path = attr.path(); + if path.is_ident("opaque") { + parsed.opaque = true; + attrs.remove(i); + continue; } i += 1; } diff --git a/crates/luaffi_impl/src/lib.rs b/crates/luaffi_impl/src/lib.rs index db2e0b4..02ee471 100644 --- a/crates/luaffi_impl/src/lib.rs +++ b/crates/luaffi_impl/src/lib.rs @@ -1,7 +1,6 @@ use darling::{FromMeta, ast::NestedMeta}; use proc_macro::TokenStream as TokenStream1; use quote::ToTokens; -use syn::parse_macro_input; mod cdef; mod metatype; @@ -17,8 +16,10 @@ pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 { } #[proc_macro_attribute] -pub fn metatype(_args: TokenStream1, input: TokenStream1) -> TokenStream1 { - metatype::transform(parse_macro_input!(input)) +pub fn metatype(args: TokenStream1, input: TokenStream1) -> TokenStream1 { + NestedMeta::parse_meta_list(args.into()) + .and_then(|meta| metatype::Args::from_list(&meta).map_err(Into::into)) + .and_then(|args| metatype::transform(args, syn::parse(input)?)) .unwrap_or_else(|err| err.into_compile_error().into_token_stream()) .into() } diff --git a/crates/luaffi_impl/src/metatype.rs b/crates/luaffi_impl/src/metatype.rs index 98bc5f3..901a7d4 100644 --- a/crates/luaffi_impl/src/metatype.rs +++ b/crates/luaffi_impl/src/metatype.rs @@ -2,19 +2,23 @@ use crate::utils::{ StringLike, ffi_crate, is_optionlike, is_primitivelike, is_stringlike, is_unit, pat_ident, syn_assert, syn_error, ty_name, }; +use darling::FromMeta; use proc_macro2::TokenStream; use quote::{ToTokens, format_ident, quote, quote_spanned}; use std::{collections::HashSet, fmt, iter}; use syn::{ext::IdentExt, spanned::Spanned, *}; -pub fn transform(mut imp: ItemImpl) -> Result { +#[derive(Debug, FromMeta)] +pub struct Args {} + +pub fn transform(args: Args, mut imp: ItemImpl) -> Result { syn_assert!( imp.generics.params.is_empty(), imp.generics, "cannot be generic (not yet implemented)" ); - let impls = generate_impls(&mut imp)?; + let impls = generate_impls(&args, &mut imp)?; let mod_name = format_ident!("__{}_metatype", ty_name(&imp.self_ty)?); Ok(quote_spanned!(imp.self_ty.span() => @@ -46,7 +50,7 @@ impl Registry { } } -fn generate_impls(imp: &mut ItemImpl) -> Result { +fn generate_impls(_args: &Args, imp: &mut ItemImpl) -> Result { let ffi = ffi_crate(); let ty = imp.self_ty.clone(); let ty_name = ty_name(&ty)?; @@ -54,6 +58,26 @@ fn generate_impls(imp: &mut ItemImpl) -> Result { let mut mms = HashSet::new(); let mut lua_drop = None; + // process lua includes + imp.attrs.retain(|attr| { + if attr.path().is_ident("include") { + if let Ok(path) = attr.parse_args::() { + registry.build.push(quote_spanned!(attr.span() => + b.include_lua(::std::include_str!(#path)); + )); + return false; + } else if let Ok(chunk) = attr.parse_args::() { + registry.build.push(quote_spanned!(attr.span() => + b.include_lua(#ffi::__internal::luaify_chunk!(#chunk)); + )); + return false; + } + } + + true + }); + + // process extern "Lua-C" ffi functions for func in get_ffi_functions(imp)? { if let Some(mm) = func.attrs.metamethod { syn_assert!( @@ -66,6 +90,7 @@ fn generate_impls(imp: &mut ItemImpl) -> Result { add_ffi_function(&mut registry, &func)?; } + // process extern "Lua" lua functions for func in get_lua_functions(imp)? { if let Some(mm) = func.attrs.metamethod { syn_assert!( @@ -82,10 +107,12 @@ fn generate_impls(imp: &mut ItemImpl) -> Result { } } + // if no #[new] metamethod is defined, inject fallback new if !mms.contains(&Metamethod::New) { inject_fallback_new(&mut registry)?; } + // inject __gc/drop inject_merged_drop(&mut registry, lua_drop.as_ref())?; let shims = ®istry.shims; @@ -279,7 +306,7 @@ fn parse_ffi_function_attrs(attrs: &mut Vec) -> Result syn_error!(attr, "implement `Drop` instead"), @@ -652,7 +679,7 @@ fn parse_lua_function_attrs(attrs: &mut Vec) -> Result syn_error!(attr, r#"cannot be applied to a lua function"#), diff --git a/crates/luaffi_impl/src/utils.rs b/crates/luaffi_impl/src/utils.rs index 356fac9..edc55ac 100644 --- a/crates/luaffi_impl/src/utils.rs +++ b/crates/luaffi_impl/src/utils.rs @@ -1,5 +1,5 @@ use std::env; -use syn::{spanned::Spanned, *}; +use syn::{ext::IdentExt, spanned::Spanned, *}; macro_rules! syn_error { ($src:expr, $($fmt:expr),+) => {{ @@ -63,7 +63,7 @@ pub fn is_primitivelike(ty: &Type) -> bool { Type::Path(path) => { if let Some(name) = path.path.get_ident() { return matches!( - name.to_string().as_str(), + name.unraw().to_string().as_str(), "bool" | "u8" | "u16" @@ -115,31 +115,37 @@ pub fn is_stringlike(ty: &Type) -> Option { && ty.mutability.is_none() && ty.lifetime.is_none() { - match *ty.elem { + Some(match *ty.elem { Type::Slice(ref slice) => { // match &[u8] if let Type::Path(ref path) = *slice.elem && let Some(name) = path.path.get_ident() - && name == "u8" { - return Some(StringLike::SliceU8); + match name.unraw().to_string().as_str() { + "u8" => StringLike::SliceU8, + _ => return None, + } + } else { + return None; } } Type::Path(ref path) => { // match &str or &BStr if let Some(name) = path.path.get_ident() { - match name.to_string().as_str() { - "str" => return Some(StringLike::Str), - "BStr" => return Some(StringLike::BStr), - _ => {} + match name.unraw().to_string().as_str() { + "str" => StringLike::Str, + "BStr" => StringLike::BStr, + _ => return None, } + } else { + return None; } } - _ => {} - } + _ => return None, + }) + } else { + None } - - None } pub fn is_optionlike(ty: &Type) -> Option<&Type> { diff --git a/src/lib.rs b/src/lib.rs index 9b6ba8e..06eb7b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,11 +10,11 @@ pub use lb::task; #[doc(hidden)] pub fn open(#[allow(unused)] rt: &mut lb::runtime::Builder) { #[cfg(feature = "task")] - rt.module::("lb:task"); + rt.module::(); #[cfg(feature = "task")] - rt.module::("lb:channel"); + rt.module::(); #[cfg(feature = "fs")] - rt.module::("lb:fs"); + rt.module::(); #[cfg(feature = "net")] - rt.module::("lb:net"); + rt.module::(); }