Let modules decide their own name via #[cdef] macro
This commit is contained in:
parent
1c1753234d
commit
0cafac0948
@ -1,7 +1,7 @@
|
||||
// use flume::{Receiver, Sender};
|
||||
use luaffi::{cdef, metatype};
|
||||
|
||||
#[cdef]
|
||||
#[cdef(module = "lb:chan")]
|
||||
pub struct lb_chanlib;
|
||||
|
||||
#[metatype]
|
||||
|
@ -15,7 +15,7 @@ use tokio::fs;
|
||||
/// ```lua
|
||||
/// local fs = require("lb:fs");
|
||||
/// ```
|
||||
#[cdef]
|
||||
#[cdef(module = "lb:fs")]
|
||||
pub struct lb_fslib;
|
||||
|
||||
#[metatype]
|
||||
|
@ -37,7 +37,7 @@ type Result<T> = std::result::Result<T, Error>;
|
||||
/// ```lua
|
||||
/// local net = require("lb:net");
|
||||
/// ```
|
||||
#[cdef]
|
||||
#[cdef(module = "lb:net")]
|
||||
pub struct lb_netlib;
|
||||
|
||||
#[metatype]
|
||||
|
@ -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<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
||||
self.registry.preload::<T>(name);
|
||||
pub fn module<T: Module>(&mut self) -> &mut Self {
|
||||
self.registry.preload::<T>();
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -158,16 +158,16 @@ impl Registry {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn preload<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
||||
pub fn preload<T: Module>(&mut self) -> &mut Self {
|
||||
assert!(T::ty() != TypeType::Void, "cannot declare void type");
|
||||
self.include::<T>();
|
||||
let ct = T::name();
|
||||
let name = <T as Module>::name();
|
||||
let ct = <T as Type>::name();
|
||||
writeln!(
|
||||
self.lua,
|
||||
r#"__preload["{name}"] = function(...) return __ct.{ct}(...); end;"#,
|
||||
)
|
||||
.unwrap();
|
||||
self
|
||||
self.include::<T>()
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<String>,
|
||||
}
|
||||
|
||||
pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
||||
let (name, impl_type, impl_cdef) = match item {
|
||||
pub fn transform(args: Args, mut item: Item) -> Result<TokenStream> {
|
||||
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<TokenStream> {
|
||||
mod #mod_name {
|
||||
use super::*;
|
||||
#impl_type
|
||||
#impl_module
|
||||
#impl_cdef
|
||||
}
|
||||
))
|
||||
@ -47,9 +56,7 @@ fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
||||
|
||||
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<TokenStream> {
|
||||
))
|
||||
}
|
||||
|
||||
fn generate_module(name: &str, ty: &Ident) -> Result<TokenStream> {
|
||||
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<TokenStream> {
|
||||
syn_assert!(
|
||||
str.generics.params.is_empty(),
|
||||
@ -155,12 +172,11 @@ fn parse_cfield_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
|
||||
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;
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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<TokenStream> {
|
||||
#[derive(Debug, FromMeta)]
|
||||
pub struct Args {}
|
||||
|
||||
pub fn transform(args: Args, mut imp: ItemImpl) -> Result<TokenStream> {
|
||||
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<TokenStream> {
|
||||
fn generate_impls(_args: &Args, imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
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<TokenStream> {
|
||||
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::<LitStr>() {
|
||||
registry.build.push(quote_spanned!(attr.span() =>
|
||||
b.include_lua(::std::include_str!(#path));
|
||||
));
|
||||
return false;
|
||||
} else if let Ok(chunk) = attr.parse_args::<Block>() {
|
||||
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<TokenStream> {
|
||||
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<TokenStream> {
|
||||
}
|
||||
}
|
||||
|
||||
// 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<Attribute>) -> Result<FfiFunctionAtt
|
||||
let mut i = 0;
|
||||
while let Some(attr) = attrs.get(i) {
|
||||
if let Some(name) = attr.path().get_ident()
|
||||
&& let Ok(method) = Metamethod::try_from(name)
|
||||
&& let Ok(method) = Metamethod::try_from(&name.unraw())
|
||||
{
|
||||
match method {
|
||||
Metamethod::Gc => syn_error!(attr, "implement `Drop` instead"),
|
||||
@ -652,7 +679,7 @@ fn parse_lua_function_attrs(attrs: &mut Vec<Attribute>) -> Result<LuaFunctionAtt
|
||||
let mut i = 0;
|
||||
while let Some(attr) = attrs.get(i) {
|
||||
if let Some(name) = attr.path().get_ident()
|
||||
&& let Ok(method) = Metamethod::try_from(name)
|
||||
&& let Ok(method) = Metamethod::try_from(&name.unraw())
|
||||
{
|
||||
match method {
|
||||
Metamethod::New => syn_error!(attr, r#"cannot be applied to a lua function"#),
|
||||
|
@ -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<StringLike> {
|
||||
&& 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> {
|
||||
|
@ -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::<task::lb_tasklib>("lb:task");
|
||||
rt.module::<task::lb_tasklib>();
|
||||
#[cfg(feature = "task")]
|
||||
rt.module::<chan::lb_chanlib>("lb:channel");
|
||||
rt.module::<chan::lb_chanlib>();
|
||||
#[cfg(feature = "fs")]
|
||||
rt.module::<fs::lb_fslib>("lb:fs");
|
||||
rt.module::<fs::lb_fslib>();
|
||||
#[cfg(feature = "net")]
|
||||
rt.module::<net::lb_netlib>("lb:net");
|
||||
rt.module::<net::lb_netlib>();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user