Let modules decide their own name via #[cdef] macro
This commit is contained in:
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user