scripting: improve macros to make it easier to create wrappers
This commit is contained in:
parent
29c68abbbb
commit
d1f1e03cbb
|
@ -1,12 +1,81 @@
|
|||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, Ident, Token};
|
||||
use syn::{braced, parenthesized, parse_macro_input, token, Ident, Path, Token};
|
||||
|
||||
pub(crate) struct HandleWrapUsage {
|
||||
pub type_path: syn::Path,
|
||||
struct FieldGetter {
|
||||
field: Ident,
|
||||
body: Option<syn::Block>,
|
||||
wrapper_type: Option<syn::Path>,
|
||||
}
|
||||
|
||||
impl FieldGetter {
|
||||
fn parse_extended(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let field_name = input.parse()?;
|
||||
|
||||
let mut s = Self {
|
||||
field: field_name,
|
||||
body: None,
|
||||
wrapper_type: None
|
||||
};
|
||||
|
||||
while input.peek(Token![,]) {
|
||||
let _: Token![,] = input.parse()?;
|
||||
|
||||
if input.peek(syn::Ident) {
|
||||
let ident: syn::Ident = input.parse()?;
|
||||
let ident_str = ident.to_string();
|
||||
let ident_str = ident_str.as_str();
|
||||
|
||||
match ident_str {
|
||||
"wrapper" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
let name: Path = input.parse()?;
|
||||
s.wrapper_type = Some(name);
|
||||
},
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(block) = input.parse::<syn::Block>() {
|
||||
s.body = Some(block);
|
||||
}
|
||||
}
|
||||
|
||||
if s.body.is_some() && s.wrapper_type.is_some() {
|
||||
return Err(syn::Error::new(input.span(), "cannot use body and wrapper_type, choose one"));
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for FieldGetter {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
Self::parse_extended(&content)
|
||||
} else {
|
||||
let field_name = input.parse()?;
|
||||
Ok(Self {
|
||||
field: field_name,
|
||||
body: None,
|
||||
wrapper_type: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HandleWrapUsage {
|
||||
type_path: syn::Path,
|
||||
/// The extra derives of the type.
|
||||
pub override_name: Option<Ident>,
|
||||
override_name: Option<Ident>,
|
||||
field_getters: Vec<FieldGetter>,
|
||||
|
||||
pub extra_builds: Option<syn::Block>,
|
||||
extra_builds: Option<syn::Block>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for HandleWrapUsage {
|
||||
|
@ -15,6 +84,7 @@ impl syn::parse::Parse for HandleWrapUsage {
|
|||
let mut s = Self {
|
||||
type_path,
|
||||
override_name: None,
|
||||
field_getters: vec![],
|
||||
extra_builds: None,
|
||||
};
|
||||
|
||||
|
@ -30,17 +100,24 @@ impl syn::parse::Parse for HandleWrapUsage {
|
|||
"name" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
let name: Ident = input.parse()?;
|
||||
s.override_name = Some(name);
|
||||
s.override_name = Some(input.parse()?);
|
||||
},
|
||||
"field_getters" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
if input.peek(token::Brace) {
|
||||
let content;
|
||||
let _braced: token::Brace = braced!(content in input);
|
||||
|
||||
let terminated = content.parse_terminated(FieldGetter::parse, Token![,])?;
|
||||
s.field_getters = terminated.into_iter().collect();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
|
||||
}
|
||||
}
|
||||
}/* else if input.parse::<Block(syn::Block) {
|
||||
let block = input.parse()?;
|
||||
s.extra_builds = block;
|
||||
} */
|
||||
}
|
||||
|
||||
if let Ok(block) = input.parse::<syn::Block>() {
|
||||
s.extra_builds = Some(block);
|
||||
|
@ -63,6 +140,34 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
|||
|
||||
let extras = input.extra_builds;
|
||||
|
||||
let custom_getters = input.field_getters.iter().map(|g| {
|
||||
let field = &g.field;
|
||||
|
||||
let field_creator = match &g.wrapper_type {
|
||||
Some(wrap) => {
|
||||
quote!(#wrap(data.#field).as_lua(lua))
|
||||
},
|
||||
None => match &g.body {
|
||||
Some(body) => {
|
||||
quote!(#body)
|
||||
},
|
||||
None => {
|
||||
quote!(data.#field.clone().as_lua(lua))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
builder.field_getter(stringify!($field), |lua, this| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
#field_creator
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[derive(Clone, Reflect)]
|
||||
pub struct #wrapper_name(pub ResHandle<#handle_name>);
|
||||
|
@ -100,6 +205,8 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
|||
Ok(name)
|
||||
});
|
||||
|
||||
#(#custom_getters)*
|
||||
|
||||
builder.method("is_watched", |_, this, ()| {
|
||||
Ok(this.is_watched())
|
||||
});
|
||||
|
|
|
@ -1,735 +1,19 @@
|
|||
use handle_macro::lua_wrap_handle_impl;
|
||||
use lua_macro::wrap_lua_struct_impl;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced};
|
||||
use syn::{parse_macro_input, Token};
|
||||
|
||||
mod mat_wrapper;
|
||||
use mat_wrapper::MatWrapper;
|
||||
mod vec_wrapper;
|
||||
use vec_wrapper::VecWrapper;
|
||||
|
||||
mod lua_macro;
|
||||
|
||||
mod handle_macro;
|
||||
|
||||
// pub use lua_macro::wrap_lua_struct;
|
||||
|
||||
pub(crate) const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type";
|
||||
pub(crate) const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
|
||||
|
||||
pub(crate) struct MetaMethod {
|
||||
pub ident: Ident,
|
||||
pub mods: Vec<Ident>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for MetaMethod {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let ident: Ident = input.parse()?;
|
||||
|
||||
let mods = if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
content.parse_terminated(Ident::parse, Token![,])?
|
||||
.into_iter().collect()
|
||||
} else { vec![] };
|
||||
|
||||
Ok(Self {
|
||||
ident,
|
||||
mods,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl MetaMethod {
|
||||
/// Returns a boolean if an identifier is a lua wrapper, and therefore also userdata
|
||||
fn is_arg_wrapper(ident: &Ident) -> bool {
|
||||
let s = ident.to_string();
|
||||
s.starts_with("Lua")
|
||||
}
|
||||
|
||||
/// Returns a boolean indiciating if the metamethod has takes in any arguments
|
||||
fn does_metamethod_have_arg(metamethod: &Ident) -> bool {
|
||||
let mm_str = metamethod.to_string();
|
||||
let mm_str = mm_str.as_str();
|
||||
match mm_str {
|
||||
"Add" | "Sub" | "Div" | "Mul" | "Mod" | "Eq" | "Shl" | "Shr" | "BAnd" | "BOr"
|
||||
| "BXor" => {
|
||||
true
|
||||
},
|
||||
"Unm" | "BNot" | "ToString" => {
|
||||
false
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the tokens of the body of the metamethod
|
||||
///
|
||||
/// Parameters
|
||||
/// * `metamethod` - The ident of the metamethod that is being implemented.
|
||||
/// * `other` - The tokens of the argument used in the metamethod.
|
||||
fn get_method_body(metamethod: &Ident, other: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||
let mm_str = metamethod.to_string();
|
||||
let mm_str = mm_str.as_str();
|
||||
match mm_str {
|
||||
"Add" | "Sub" | "Div" | "Mul" | "Mod" => {
|
||||
let symbol = match mm_str {
|
||||
"Add" => quote!(+),
|
||||
"Sub" => quote!(-),
|
||||
"Div" => quote!(/),
|
||||
"Mul" => quote!(*),
|
||||
"Mod" => quote!(%),
|
||||
_ => unreachable!(), // the string was just checked to be one of these
|
||||
};
|
||||
|
||||
quote! {
|
||||
Ok(Self(this.0 #symbol #other))
|
||||
}
|
||||
},
|
||||
"Unm" => {
|
||||
quote! {
|
||||
Ok(Self(-this.0))
|
||||
}
|
||||
},
|
||||
"Eq" => {
|
||||
quote! {
|
||||
Ok(this.0 == #other)
|
||||
}
|
||||
},
|
||||
"Shl" => {
|
||||
quote! {
|
||||
Ok(Self(this.0 << #other))
|
||||
}
|
||||
}
|
||||
"Shr" => {
|
||||
quote! {
|
||||
Ok(Self(this.0 >> #other))
|
||||
}
|
||||
},
|
||||
"BAnd" | "BOr" | "BXor" => {
|
||||
let symbol = match mm_str {
|
||||
"BAnd" => {
|
||||
quote!(&)
|
||||
},
|
||||
"BOr" => {
|
||||
quote!(|)
|
||||
},
|
||||
"BXor" => {
|
||||
quote!(^)
|
||||
},
|
||||
_ => unreachable!() // the string was just checked to be one of these
|
||||
};
|
||||
|
||||
quote! {
|
||||
Ok(Self(this.0 #symbol #other))
|
||||
}
|
||||
},
|
||||
"BNot" => {
|
||||
quote! {
|
||||
Ok(Self(!this.0))
|
||||
}
|
||||
},
|
||||
"ToString" => {
|
||||
quote! {
|
||||
Ok(format!("{:?}", this.0))
|
||||
}
|
||||
},
|
||||
_ => syn::Error::new_spanned(metamethod,
|
||||
"unsupported auto implementation of metamethod").to_compile_error(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_body_for_arg(mt_ident: &Ident, arg_ident: &Ident, arg_param: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||
let other: proc_macro2::TokenStream = if Self::is_arg_wrapper(arg_ident) {
|
||||
// Lua wrappers must be dereferenced
|
||||
quote! {
|
||||
#arg_param.0
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#arg_param
|
||||
}
|
||||
};
|
||||
Self::get_method_body(&mt_ident, other)
|
||||
}
|
||||
|
||||
pub fn to_tokens(&self, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
|
||||
let wrapped_str = &wrapper_ident.to_string()[3..]; // removes starting 'Lua' from name
|
||||
let mt_ident = &self.ident;
|
||||
let mt_lua_name = mt_ident.to_string().to_lowercase();
|
||||
|
||||
if self.mods.is_empty() {
|
||||
let other = quote! {
|
||||
v.0
|
||||
};
|
||||
let body = Self::get_method_body(&self.ident, other);
|
||||
|
||||
if Self::does_metamethod_have_arg(&self.ident) {
|
||||
quote! {
|
||||
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| {
|
||||
#body
|
||||
});
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, ()| {
|
||||
#body
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} else if self.mods.len() == 1 {
|
||||
let first = self.mods.iter().next().unwrap();
|
||||
let body = Self::get_body_for_arg(&self.ident, first, quote!(v));
|
||||
|
||||
quote! {
|
||||
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| {
|
||||
#body
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// an optional match arm that matches elua::Value:Number
|
||||
let number_arm = {
|
||||
let num_ident = self.mods.iter().find(|i| {
|
||||
let is = i.to_string();
|
||||
let is = is.as_str();
|
||||
match is {
|
||||
"u8" | "u16" | "u32" | "u64" | "u128"
|
||||
| "i8" | "i16" | "i32" | "i64" | "i128"
|
||||
| "f32" | "f64" => true,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(num_ident) = num_ident {
|
||||
let body = Self::get_body_for_arg(&self.ident, num_ident, quote!(n as #num_ident));
|
||||
|
||||
quote! {
|
||||
elua::Value::Number(n) => {
|
||||
#body
|
||||
},
|
||||
}
|
||||
} else { quote!() }
|
||||
};
|
||||
|
||||
let userdata_arm = {
|
||||
let wrappers: Vec<&Ident> = self.mods.iter()
|
||||
.filter(|i| Self::is_arg_wrapper(i))
|
||||
.collect();
|
||||
|
||||
let if_statements = wrappers.iter().map(|i| {
|
||||
let body = Self::get_method_body(&self.ident, quote!(other.0));
|
||||
|
||||
quote! {
|
||||
if let Ok(other) = ud.as_ref::<#i>() {
|
||||
#body
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
quote! {
|
||||
elua::Value::Userdata(ud) => {
|
||||
#(#if_statements else)*
|
||||
// this is the body of the else statement
|
||||
{
|
||||
// try to get the name of the userdata for the error message
|
||||
if let Ok(mt) = ud.get_metatable() {
|
||||
if let Ok(name) = mt.get::<_, String>("__name") {
|
||||
return Err(elua::Error::BadArgument {
|
||||
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
|
||||
arg_index: 2,
|
||||
arg_name: Some("rhs".to_string()),
|
||||
error: Arc::new(elua::Error::Runtime(
|
||||
format!("cannot multiply with unknown userdata named {}", name)
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Err(elua::Error::BadArgument {
|
||||
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
|
||||
arg_index: 2,
|
||||
arg_name: Some("rhs".to_string()),
|
||||
error: Arc::new(
|
||||
elua::Error::runtime("cannot multiply with unknown userdata")
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (elua::Value,)| {
|
||||
match v {
|
||||
#number_arm
|
||||
#userdata_arm
|
||||
_ => Err(elua::Error::BadArgument {
|
||||
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
|
||||
arg_index: 2,
|
||||
arg_name: Some("rhs".to_string()),
|
||||
error: Arc::new(
|
||||
elua::Error::Runtime(format!("cannot multiply with {}", v.type_name()))
|
||||
)
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct VecWrapper {
|
||||
|
||||
}
|
||||
|
||||
impl VecWrapper {
|
||||
fn vec_size(&self, wrapper_ident: &Ident) -> usize {
|
||||
let name = wrapper_ident.to_string();
|
||||
name[name.len() - 1..].parse::<usize>()
|
||||
.or_else(|_| name[name.len() - 2.. name.len() - 1].parse::<usize>())
|
||||
.expect("Failure to grab Vec size from ident name")
|
||||
}
|
||||
|
||||
/// Returns the token stream of the type of the axis of the vec (Vec2 vs IVec2 vs I64Vec2, etc.)
|
||||
fn vec_axis_type(&self, wrapper_ident: &Ident) -> &'static str {
|
||||
let name = wrapper_ident.to_string();
|
||||
let start = name.find("Vec").unwrap();
|
||||
|
||||
let before = &name[start - 1.. start];
|
||||
match before {
|
||||
"D" => return "f64",
|
||||
"I" => return "i32",
|
||||
"U" => return "u32",
|
||||
"B" => return "bool",
|
||||
_ => {},
|
||||
}
|
||||
//println!("before is {before}");
|
||||
|
||||
let three_before = &name[start - 3.. start];
|
||||
match three_before {
|
||||
"I64" => return "i64",
|
||||
"U64" => return "u64",
|
||||
_ => {},
|
||||
}
|
||||
//println!("three before is {three_before}");
|
||||
|
||||
"f32"
|
||||
}
|
||||
|
||||
pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
|
||||
let mut consts = vec![quote!(ZERO), quote!(ONE), quote!(X),
|
||||
quote!(Y), ]; // , quote!(AXES)
|
||||
|
||||
let vec_size = self.vec_size(wrapper_ident);
|
||||
let axis_type_name = self.vec_axis_type(wrapper_ident);
|
||||
|
||||
if axis_type_name.contains("b") {
|
||||
return quote! {
|
||||
builder.field("FALSE", #wrapper_ident(#wrapped_path::FALSE));
|
||||
builder.field("TRUE", #wrapper_ident(#wrapped_path::TRUE));
|
||||
};
|
||||
}
|
||||
|
||||
if vec_size >= 3 {
|
||||
consts.push(quote!(Z));
|
||||
|
||||
// no negative numbers for unsigned vecs
|
||||
if !axis_type_name.contains("u") {
|
||||
consts.push(quote!(NEG_Z));
|
||||
}
|
||||
}
|
||||
|
||||
if vec_size == 4 {
|
||||
consts.push(quote!(W));
|
||||
|
||||
// no negative numbers for unsigned vecs
|
||||
if !axis_type_name.contains("u") {
|
||||
consts.push(quote!(NEG_W));
|
||||
}
|
||||
}
|
||||
|
||||
// no negative numbers for unsigned vecs
|
||||
if !axis_type_name.contains("u") {
|
||||
consts.push(quote!(NEG_X));
|
||||
consts.push(quote!(NEG_Y));
|
||||
consts.push(quote!(NEG_ONE));
|
||||
}
|
||||
|
||||
if axis_type_name.contains("f") {
|
||||
consts.push(quote!(NAN))
|
||||
}
|
||||
|
||||
let const_tokens = consts.iter().map(|cnst| {
|
||||
let const_name = cnst.to_string();
|
||||
|
||||
quote! {
|
||||
builder.field(#const_name, #wrapper_ident(#wrapped_path::#cnst));
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#(#const_tokens)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_method_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
|
||||
let vec_size = self.vec_size(wrapper_ident);
|
||||
let axis_type_name = self.vec_axis_type(wrapper_ident);
|
||||
// methods that only some vecs have
|
||||
let mut optional_methods = vec![];
|
||||
|
||||
// boolean vectors dont have much :(
|
||||
if axis_type_name.contains("b") {
|
||||
return quote!(); // TODO: all, any, bitmask, splat
|
||||
}
|
||||
|
||||
if axis_type_name.contains("f") {
|
||||
let type_id = Ident::new(axis_type_name, Span::call_site());
|
||||
|
||||
optional_methods.push(
|
||||
quote! {
|
||||
builder.method("clamp_length",
|
||||
|_, this, (min, max): (#type_id, #type_id)| {
|
||||
Ok(#wrapper_ident(this.clamp_length(min, max)))
|
||||
});
|
||||
|
||||
builder.method("abs_diff_eq",
|
||||
|_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| {
|
||||
Ok(this.abs_diff_eq(rhs.0, max_abs_diff))
|
||||
});
|
||||
|
||||
builder.method("ceil",
|
||||
|_, this, (): ()| {
|
||||
Ok(#wrapper_ident(this.ceil()))
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if vec_size != 4 {
|
||||
optional_methods.push(
|
||||
quote! {
|
||||
builder.method("angle_between",
|
||||
|_, this, (rhs,): (#wrapper_ident,)| {
|
||||
Ok(this.angle_between(rhs.0))
|
||||
});
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !axis_type_name.contains("u") {
|
||||
optional_methods.push(
|
||||
quote! {
|
||||
builder.method("abs",
|
||||
|_, this, (): ()| {
|
||||
Ok(#wrapper_ident(this.abs()))
|
||||
});
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let optional_methods_iter = optional_methods.iter();
|
||||
quote! {
|
||||
|
||||
|
||||
builder.method("clamp",
|
||||
|_, this, (min, max): (#wrapper_ident, #wrapper_ident)| {
|
||||
Ok(#wrapper_ident(this.clamp(min.0, max.0)))
|
||||
});
|
||||
|
||||
// TODO: Not all Vecs have this
|
||||
/* builder.method("clamp_length",
|
||||
|_, this, (min, max): (f32, f32)| {
|
||||
Ok(#wrapper_ident(this.clamp_length(min, max)))
|
||||
}); */
|
||||
|
||||
|
||||
builder.method("to_array",
|
||||
|_, this, (): ()| {
|
||||
Ok(this.to_array())
|
||||
});
|
||||
|
||||
#(#optional_methods_iter)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WrapUsage {
|
||||
pub type_path: Path,
|
||||
/// The extra derives of the type.
|
||||
pub derive_idents: Punctuated<Ident, Token![,]>,
|
||||
/// The field idents of the type that will be exposed with gets and sets
|
||||
pub field_idents: Punctuated<Ident, Token![,]>,
|
||||
pub skip_new_fn: bool,
|
||||
/// The identifiers that are taken as parameters in the types 'new' function
|
||||
pub new_fn_idents: Punctuated<Ident, Token![,]>,
|
||||
pub meta_method_idents: Punctuated<MetaMethod, Token![,]>,
|
||||
|
||||
pub matrix: Option<MatWrapper>,
|
||||
pub vec: Option<VecWrapper>,
|
||||
|
||||
pub custom_methods: Option<syn::Block>,
|
||||
pub custom_fields: Option<syn::Block>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for WrapUsage {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let type_path: Path = input.parse()?;
|
||||
let mut s = Self {
|
||||
type_path,
|
||||
derive_idents: Punctuated::default(),
|
||||
field_idents: Punctuated::default(),
|
||||
skip_new_fn: false,
|
||||
new_fn_idents: Punctuated::default(),
|
||||
meta_method_idents: Punctuated::default(),
|
||||
matrix: None,
|
||||
vec: None,
|
||||
custom_methods: None,
|
||||
custom_fields: None,
|
||||
};
|
||||
/* let mut derive_idents = None;
|
||||
let mut field_idents = None;
|
||||
let mut new_fn_idents = None; */
|
||||
|
||||
while input.peek(Token![,]) {
|
||||
let _: Token![,] = input.parse()?;
|
||||
//println!("Peeked a , ({:?})", input);
|
||||
|
||||
if input.peek(syn::Ident) {
|
||||
let ident: Ident = input.parse()?;
|
||||
let ident_str = ident.to_string();
|
||||
let ident_str = ident_str.as_str();
|
||||
|
||||
match ident_str {
|
||||
"derives" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let derives: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.derive_idents = derives;
|
||||
//println!("read derives: {:?}", s.derive_idents);
|
||||
}
|
||||
},
|
||||
"fields" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let fields: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.field_idents = fields;
|
||||
//println!("read fields: {:?}", s.field_idents);
|
||||
}
|
||||
},
|
||||
"new" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let fields: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.new_fn_idents = fields;
|
||||
//println!("read fields: {:?}", s.new_fn_idents);
|
||||
}
|
||||
},
|
||||
"no_new" => {
|
||||
s.skip_new_fn = true;
|
||||
},
|
||||
"matrix" => {
|
||||
if input.peek(token::Brace) {
|
||||
let content;
|
||||
let _braces = braced!(content in input);
|
||||
s.matrix = Some(content.parse()?);
|
||||
}
|
||||
},
|
||||
"metamethods" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _bracket: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let meta_methods: Punctuated<MetaMethod, Token![,]> = content.parse_terminated(MetaMethod::parse, Token![,])?;
|
||||
s.meta_method_idents = meta_methods;
|
||||
}
|
||||
},
|
||||
"custom_methods" => {
|
||||
let methods_block = input.parse()?;
|
||||
s.custom_methods = Some(methods_block);
|
||||
},
|
||||
"custom_fields" => {
|
||||
let block = input.parse()?;
|
||||
s.custom_fields = Some(block);
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a wrapper type for a VecN from the engine math library.
|
||||
#[proc_macro]
|
||||
pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as WrapUsage);
|
||||
|
||||
let path: Path = input.type_path;
|
||||
let type_name = &path.segments.last()
|
||||
.expect("Failure to find typename in macro usage!")
|
||||
.ident;
|
||||
let wrapper_typename = Ident::new(&format!("Lua{}", type_name), Span::call_site());
|
||||
|
||||
let vec_wrapper = {
|
||||
let name_str = type_name.to_string();
|
||||
if name_str.contains("Vec") {
|
||||
Some(VecWrapper {})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
// TODO: fix this so it doesn't cause a stack overflow
|
||||
/* let vec_wrapper_fields = vec_wrapper.as_ref().map(|vec|
|
||||
vec.to_field_tokens(&path, &wrapper_typename)); */
|
||||
let vec_wrapper_fields: Option<proc_macro2::TokenStream> = None;
|
||||
let vec_wrapper_methods = vec_wrapper.as_ref().map(|vec|
|
||||
vec.to_method_tokens(&path, &wrapper_typename));
|
||||
|
||||
let derive_idents_iter = input.derive_idents.iter();
|
||||
let custom_methods = input.custom_methods;
|
||||
let custom_fields = input.custom_fields;
|
||||
|
||||
let field_get_set_pairs = input.field_idents.iter().map(|i| {
|
||||
let is = i.to_string();
|
||||
quote! {
|
||||
builder.field_getter(#is, |_, this| {
|
||||
Ok(this.#i)
|
||||
});
|
||||
builder.field_setter(#is, |_, this, #i| {
|
||||
this.#i = #i;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let new_fn_idents = {
|
||||
let idents = if input.new_fn_idents.is_empty() {
|
||||
input.field_idents.iter()
|
||||
} else {
|
||||
input.new_fn_idents.iter()
|
||||
};
|
||||
|
||||
let idents_c = idents.clone();
|
||||
|
||||
if !input.skip_new_fn {
|
||||
quote! {
|
||||
builder.function("new", |_, ( #(#idents_c),* )| {
|
||||
Ok(#wrapper_typename(#path::new( #(#idents),* )))
|
||||
});
|
||||
}
|
||||
} else { quote!() }
|
||||
};
|
||||
|
||||
let matrix_wrapper_methods = input.matrix.as_ref().map(|m|
|
||||
m.to_method_tokens(&path, &wrapper_typename));
|
||||
let matrix_wrapper_fields = input.matrix.as_ref().map(|m|
|
||||
m.to_field_tokens(&path, &wrapper_typename));
|
||||
|
||||
let meta_method_idents = {
|
||||
let idents = input.meta_method_idents.iter().map(|metamethod| {
|
||||
metamethod.to_tokens(&wrapper_typename)
|
||||
});
|
||||
|
||||
quote! {
|
||||
#(#idents)*
|
||||
}
|
||||
};
|
||||
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#[derive(Clone, Copy, lyra_reflect::Reflect, #(#derive_idents_iter),*)]
|
||||
pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path);
|
||||
|
||||
impl std::ops::Deref for #wrapper_typename {
|
||||
type Target = #path;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for #wrapper_typename {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> elua::FromLua<'lua> for #wrapper_typename {
|
||||
fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
|
||||
match value {
|
||||
elua::Value::Userdata(ud) => Ok(*ud.as_ref::<Self>()?),
|
||||
_ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl elua::Userdata for #wrapper_typename {
|
||||
fn name() -> String {
|
||||
stringify!(#type_name).to_string()
|
||||
}
|
||||
|
||||
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
|
||||
#(#field_get_set_pairs)*
|
||||
|
||||
#matrix_wrapper_fields
|
||||
#vec_wrapper_fields
|
||||
|
||||
#custom_fields
|
||||
|
||||
#new_fn_idents
|
||||
|
||||
builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
|
||||
Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone())))
|
||||
});
|
||||
|
||||
builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
|
||||
Ok(crate::ScriptBorrow::from_component::<#path>(None))
|
||||
});
|
||||
|
||||
#meta_method_idents
|
||||
|
||||
#matrix_wrapper_methods
|
||||
#vec_wrapper_methods
|
||||
|
||||
#custom_methods
|
||||
}
|
||||
}
|
||||
|
||||
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
let t = std::any::TypeId::of::<#path>();
|
||||
println!("Got id of {}, it is {:?}", stringify!(#path), t);
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> elua::FromLuaVec<'a> for #wrapper_typename {
|
||||
fn from_lua_value_vec(state: &'a elua::State, mut values: elua::ValueVec<'a>) -> elua::Result<Self> {
|
||||
use elua::FromLua;
|
||||
|
||||
if let Some(val) = values.pop_front() {
|
||||
#wrapper_typename::from_lua(state, val)
|
||||
} else {
|
||||
Err(elua::Error::IncorrectArgCount {
|
||||
arg_expected: 1,
|
||||
arg_count: values.len() as i32,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn wrap_lua_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
wrap_lua_struct_impl(input)
|
||||
|
@ -738,4 +22,35 @@ pub fn wrap_lua_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
|||
#[proc_macro]
|
||||
pub fn lua_wrap_handle(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
lua_wrap_handle_impl(input)
|
||||
}
|
||||
|
||||
pub(crate) struct VecExtensionInputs {
|
||||
#[allow(dead_code)]
|
||||
pub type_path: syn::Path,
|
||||
pub wrapper_ident: syn::Ident,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for VecExtensionInputs {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let type_path: syn::Path = input.parse()?;
|
||||
let _comma: Token![,] = input.parse()?;
|
||||
let wrapper_ident: syn::Ident = input.parse()?;
|
||||
|
||||
Ok(Self {
|
||||
type_path,
|
||||
wrapper_ident
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn lua_vec_wrap_extension(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as VecExtensionInputs);
|
||||
|
||||
let wrapper = VecWrapper;
|
||||
let method_tokens = wrapper.to_method_tokens(&input.wrapper_ident);
|
||||
|
||||
quote! {
|
||||
#method_tokens
|
||||
}.into()
|
||||
}
|
|
@ -1,8 +1,397 @@
|
|||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Path, Ident};
|
||||
use syn::{parenthesized, parse_macro_input, punctuated::Punctuated, token, Ident, Path, Token};
|
||||
|
||||
use crate::{VecWrapper, WrapUsage, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
|
||||
use crate::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum SkipType {
|
||||
/// Skips implementing
|
||||
LuaReflect
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for SkipType {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let name: Ident = input.parse()?;
|
||||
let name_str = name.to_string();
|
||||
|
||||
match name_str.as_str() {
|
||||
"lua_reflect" => Ok(Self::LuaReflect),
|
||||
_ => {
|
||||
Err(syn::Error::new_spanned(name, "unknown skip type"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct MetaMethod {
|
||||
name: Ident,
|
||||
// If empty, assume `Self`
|
||||
arg: Vec<Ident>,
|
||||
}
|
||||
|
||||
impl MetaMethod {
|
||||
/// Returns a boolean if an identifier is a lua wrapper, and therefore also userdata
|
||||
fn is_arg_wrapper(ident: &Ident) -> bool {
|
||||
let s = ident.to_string();
|
||||
s.starts_with("Lua")
|
||||
}
|
||||
|
||||
/// Returns a boolean indiciating if the metamethod has takes in any arguments
|
||||
fn does_metamethod_have_arg(metamethod: &Ident) -> bool {
|
||||
let mm_str = metamethod.to_string();
|
||||
let mm_str = mm_str.as_str();
|
||||
match mm_str {
|
||||
"Add" | "Sub" | "Div" | "Mul" | "Mod" | "Eq" | "Shl" | "Shr" | "BAnd" | "BOr"
|
||||
| "BXor" => {
|
||||
true
|
||||
},
|
||||
"Unm" | "BNot" | "ToString" => {
|
||||
false
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the tokens of the body of the metamethod
|
||||
///
|
||||
/// Parameters
|
||||
/// * `metamethod` - The ident of the metamethod that is being implemented.
|
||||
/// * `other` - The tokens of the argument used in the metamethod.
|
||||
fn get_method_body(metamethod: &Ident, other: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||
let mm_str = metamethod.to_string();
|
||||
let mm_str = mm_str.as_str();
|
||||
match mm_str {
|
||||
"Add" | "Sub" | "Div" | "Mul" | "Mod" => {
|
||||
let symbol = match mm_str {
|
||||
"Add" => quote!(+),
|
||||
"Sub" => quote!(-),
|
||||
"Div" => quote!(/),
|
||||
"Mul" => quote!(*),
|
||||
"Mod" => quote!(%),
|
||||
_ => unreachable!(), // the string was just checked to be one of these
|
||||
};
|
||||
|
||||
quote! {
|
||||
Ok(Self(this.0 #symbol #other))
|
||||
}
|
||||
},
|
||||
"Unm" => {
|
||||
quote! {
|
||||
Ok(Self(-this.0))
|
||||
}
|
||||
},
|
||||
"Eq" => {
|
||||
quote! {
|
||||
Ok(this.0 == #other)
|
||||
}
|
||||
},
|
||||
"Shl" => {
|
||||
quote! {
|
||||
Ok(Self(this.0 << #other))
|
||||
}
|
||||
}
|
||||
"Shr" => {
|
||||
quote! {
|
||||
Ok(Self(this.0 >> #other))
|
||||
}
|
||||
},
|
||||
"BAnd" | "BOr" | "BXor" => {
|
||||
let symbol = match mm_str {
|
||||
"BAnd" => {
|
||||
quote!(&)
|
||||
},
|
||||
"BOr" => {
|
||||
quote!(|)
|
||||
},
|
||||
"BXor" => {
|
||||
quote!(^)
|
||||
},
|
||||
_ => unreachable!() // the string was just checked to be one of these
|
||||
};
|
||||
|
||||
quote! {
|
||||
Ok(Self(this.0 #symbol #other))
|
||||
}
|
||||
},
|
||||
"BNot" => {
|
||||
quote! {
|
||||
Ok(Self(!this.0))
|
||||
}
|
||||
},
|
||||
"ToString" => {
|
||||
quote! {
|
||||
Ok(format!("{:?}", this.0))
|
||||
}
|
||||
},
|
||||
_ => syn::Error::new_spanned(metamethod,
|
||||
"unsupported auto implementation of metamethod").to_compile_error(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_body_for_arg(mt_ident: &Ident, arg_ident: &Ident, arg_param: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||
let other: proc_macro2::TokenStream = if Self::is_arg_wrapper(arg_ident) {
|
||||
// Lua wrappers must be dereferenced
|
||||
quote! {
|
||||
#arg_param.0
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#arg_param
|
||||
}
|
||||
};
|
||||
Self::get_method_body(&mt_ident, other)
|
||||
}
|
||||
|
||||
pub fn to_tokens(&self, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
|
||||
let wrapped_str = &wrapper_ident.to_string()[3..]; // removes starting 'Lua' from name
|
||||
let mt_ident = &self.name;
|
||||
let mt_lua_name = mt_ident.to_string().to_lowercase();
|
||||
|
||||
if self.arg.is_empty() {
|
||||
let other = quote! {
|
||||
v.0
|
||||
};
|
||||
let body = Self::get_method_body(&self.name, other);
|
||||
|
||||
if Self::does_metamethod_have_arg(&self.name) {
|
||||
quote! {
|
||||
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| {
|
||||
#body
|
||||
});
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, ()| {
|
||||
#body
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} else if self.arg.len() == 1 {
|
||||
let first = self.arg.iter().next().unwrap();
|
||||
let body = Self::get_body_for_arg(&self.name, first, quote!(v));
|
||||
|
||||
quote! {
|
||||
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| {
|
||||
#body
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// an optional match arm that matches elua::Value:Number
|
||||
let number_arm = {
|
||||
let num_ident = self.arg.iter().find(|i| {
|
||||
let is = i.to_string();
|
||||
let is = is.as_str();
|
||||
match is {
|
||||
"u8" | "u16" | "u32" | "u64" | "u128"
|
||||
| "i8" | "i16" | "i32" | "i64" | "i128"
|
||||
| "f32" | "f64" => true,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(num_ident) = num_ident {
|
||||
let body = Self::get_body_for_arg(&self.name, num_ident, quote!(n as #num_ident));
|
||||
|
||||
quote! {
|
||||
elua::Value::Number(n) => {
|
||||
#body
|
||||
},
|
||||
}
|
||||
} else { quote!() }
|
||||
};
|
||||
|
||||
let userdata_arm = {
|
||||
let wrappers: Vec<&Ident> = self.arg.iter()
|
||||
.filter(|i| Self::is_arg_wrapper(i))
|
||||
.collect();
|
||||
|
||||
let if_statements = wrappers.iter().map(|i| {
|
||||
let body = Self::get_method_body(&self.name, quote!(other.0));
|
||||
|
||||
quote! {
|
||||
if let Ok(other) = ud.as_ref::<#i>() {
|
||||
#body
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
quote! {
|
||||
elua::Value::Userdata(ud) => {
|
||||
#(#if_statements else)*
|
||||
// this is the body of the else statement
|
||||
{
|
||||
// try to get the name of the userdata for the error message
|
||||
if let Ok(mt) = ud.get_metatable() {
|
||||
if let Ok(name) = mt.get::<_, String>("__name") {
|
||||
return Err(elua::Error::BadArgument {
|
||||
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
|
||||
arg_index: 2,
|
||||
arg_name: Some("rhs".to_string()),
|
||||
error: std::sync::Arc::new(elua::Error::Runtime(
|
||||
format!("cannot multiply with unknown userdata named {}", name)
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Err(elua::Error::BadArgument {
|
||||
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
|
||||
arg_index: 2,
|
||||
arg_name: Some("rhs".to_string()),
|
||||
error: std::sync::Arc::new(
|
||||
elua::Error::runtime("cannot multiply with unknown userdata")
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (elua::Value,)| {
|
||||
match v {
|
||||
#number_arm
|
||||
#userdata_arm
|
||||
_ => Err(elua::Error::BadArgument {
|
||||
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
|
||||
arg_index: 2,
|
||||
arg_name: Some("rhs".to_string()),
|
||||
error: std::sync::Arc::new(
|
||||
elua::Error::Runtime(format!("cannot multiply with {}", v.type_name()))
|
||||
)
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for MetaMethod {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let name: Ident = input.parse()?;
|
||||
|
||||
let mut s = Self {
|
||||
name,
|
||||
arg: vec![],
|
||||
};
|
||||
|
||||
// try to parse args
|
||||
if input.peek(syn::token::Paren) {
|
||||
let content;
|
||||
let _parens: syn::token::Paren = parenthesized!(content in input);
|
||||
|
||||
let arg: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.arg = arg.into_iter().collect(); // convert to Vec<Ident>
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
struct WrapUsage {
|
||||
type_path: syn::Path,
|
||||
/// The extra derives of the type.
|
||||
override_name: Option<Ident>,
|
||||
auto_fields: Vec<Ident>,
|
||||
auto_derives: Vec<Ident>,
|
||||
auto_new: bool,
|
||||
meta_methods: Vec<MetaMethod>,
|
||||
skips: Vec<SkipType>,
|
||||
|
||||
extra_builds: Option<syn::Block>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for WrapUsage {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let type_path: syn::Path = input.parse()?;
|
||||
let mut s = Self {
|
||||
type_path,
|
||||
override_name: None,
|
||||
auto_fields: vec![],
|
||||
auto_derives: vec![],
|
||||
extra_builds: None,
|
||||
auto_new: false,
|
||||
meta_methods: vec![],
|
||||
skips: vec![],
|
||||
};
|
||||
|
||||
let mut new_ident = None;
|
||||
|
||||
while input.peek(Token![,]) {
|
||||
let _: Token![,] = input.parse()?;
|
||||
|
||||
if input.peek(syn::Ident) {
|
||||
let ident: syn::Ident = input.parse()?;
|
||||
let ident_str = ident.to_string();
|
||||
let ident_str = ident_str.as_str();
|
||||
|
||||
match ident_str {
|
||||
"name" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
let name: Ident = input.parse()?;
|
||||
s.override_name = Some(name);
|
||||
},
|
||||
"new" => {
|
||||
s.auto_new = true;
|
||||
new_ident = Some(ident.clone());
|
||||
},
|
||||
"skip" => {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let terminated = content.parse_terminated(SkipType::parse, Token![,])?;
|
||||
s.skips = terminated.into_iter().collect();
|
||||
},
|
||||
"derives" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let derives: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.auto_derives = derives.into_iter().collect();
|
||||
}
|
||||
},
|
||||
"fields" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let fields: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.auto_fields = fields.into_iter().collect();
|
||||
}
|
||||
},
|
||||
"metamethods" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _bracket: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let meta_methods: Punctuated<MetaMethod, Token![,]> = content.parse_terminated(MetaMethod::parse, Token![,])?;
|
||||
s.meta_methods = meta_methods.into_iter().collect();
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(block) = input.parse::<syn::Block>() {
|
||||
s.extra_builds = Some(block);
|
||||
}
|
||||
}
|
||||
|
||||
if s.auto_new && s.auto_fields.is_empty() {
|
||||
return Err(syn::Error::new_spanned(new_ident.unwrap(), "must specify 'fields' when auto creating new function"));
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a wrapper type for a VecN from the engine math library.
|
||||
pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
|
@ -12,28 +401,13 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
let type_name = &path.segments.last()
|
||||
.expect("Failure to find typename in macro usage!")
|
||||
.ident;
|
||||
let wrapper_typename = Ident::new(&format!("Lua{}", type_name), Span::call_site());
|
||||
let wrapper_typename = input.override_name
|
||||
.unwrap_or_else(|| Ident::new(&format!("Lua{}", type_name), Span::call_site()));
|
||||
|
||||
let vec_wrapper = {
|
||||
let name_str = type_name.to_string();
|
||||
if name_str.contains("Vec") {
|
||||
Some(VecWrapper {})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
// TODO: fix this so it doesn't cause a stack overflow
|
||||
/* let vec_wrapper_fields = vec_wrapper.as_ref().map(|vec|
|
||||
vec.to_field_tokens(&path, &wrapper_typename)); */
|
||||
let vec_wrapper_fields: Option<proc_macro2::TokenStream> = None;
|
||||
let vec_wrapper_methods = vec_wrapper.as_ref().map(|vec|
|
||||
vec.to_method_tokens(&path, &wrapper_typename));
|
||||
let derive_idents_iter = input.auto_derives.iter();
|
||||
let extra_builds = input.extra_builds;
|
||||
|
||||
let derive_idents_iter = input.derive_idents.iter();
|
||||
let custom_methods = input.custom_methods;
|
||||
let custom_fields = input.custom_fields;
|
||||
|
||||
let field_get_set_pairs = input.field_idents.iter().map(|i| {
|
||||
let field_get_set_pairs = input.auto_fields.iter().map(|i| {
|
||||
let is = i.to_string();
|
||||
quote! {
|
||||
builder.field_getter(#is, |_, this| {
|
||||
|
@ -46,41 +420,50 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
}
|
||||
});
|
||||
|
||||
let new_fn_idents = {
|
||||
let idents = if input.new_fn_idents.is_empty() {
|
||||
input.field_idents.iter()
|
||||
} else {
|
||||
input.new_fn_idents.iter()
|
||||
};
|
||||
// the tokens for the new function
|
||||
let new_func_tokens = if input.auto_new {
|
||||
let arg_names = input.auto_fields.iter().map(|i| {
|
||||
Ident::new(&i.to_string().to_lowercase(), Span::call_site())
|
||||
});
|
||||
|
||||
let idents_c = idents.clone();
|
||||
let arg_names_clone = arg_names.clone();
|
||||
|
||||
if !input.skip_new_fn {
|
||||
quote! {
|
||||
builder.function("new", |_, ( #(#idents_c),* )| {
|
||||
Ok(#wrapper_typename(#path::new( #(#idents),* )))
|
||||
});
|
||||
}
|
||||
} else { quote!() }
|
||||
};
|
||||
quote! {
|
||||
// arguments for function are not specified since they can be implied from the call
|
||||
// to new(...)
|
||||
builder.function("new", |_, ( #(#arg_names_clone),* )| {
|
||||
Ok(#wrapper_typename(#path::new( #(#arg_names),* )))
|
||||
});
|
||||
}
|
||||
|
||||
let matrix_wrapper_methods = input.matrix.as_ref().map(|m|
|
||||
m.to_method_tokens(&path, &wrapper_typename));
|
||||
let matrix_wrapper_fields = input.matrix.as_ref().map(|m|
|
||||
m.to_field_tokens(&path, &wrapper_typename));
|
||||
} else { quote!() };
|
||||
|
||||
let meta_method_idents = {
|
||||
let idents = input.meta_method_idents.iter().map(|metamethod| {
|
||||
metamethod.to_tokens(&wrapper_typename)
|
||||
let meta_methods_tokens = {
|
||||
let method_tokens = input.meta_methods.iter().map(|mm| {
|
||||
mm.to_tokens(&wrapper_typename)
|
||||
});
|
||||
|
||||
quote! {
|
||||
#(#idents)*
|
||||
#(#method_tokens)*
|
||||
}
|
||||
};
|
||||
|
||||
let lua_reflects = if input.skips.contains(&SkipType::LuaReflect) {
|
||||
quote!()
|
||||
} else {
|
||||
quote! {
|
||||
builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
|
||||
Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone())))
|
||||
});
|
||||
|
||||
builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
|
||||
Ok(crate::ScriptBorrow::from_component::<#path>(None))
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#[derive(Clone, Copy, lyra_reflect::Reflect, #(#derive_idents_iter),*)]
|
||||
#[derive(Clone, lyra_reflect::Reflect, #(#derive_idents_iter),*)]
|
||||
pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path);
|
||||
|
||||
impl std::ops::Deref for #wrapper_typename {
|
||||
|
@ -100,7 +483,7 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
impl<'lua> elua::FromLua<'lua> for #wrapper_typename {
|
||||
fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
|
||||
match value {
|
||||
elua::Value::Userdata(ud) => Ok(*ud.as_ref::<Self>()?),
|
||||
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
|
||||
_ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()),
|
||||
}
|
||||
}
|
||||
|
@ -113,36 +496,17 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
|
||||
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
|
||||
#(#field_get_set_pairs)*
|
||||
#lua_reflects
|
||||
|
||||
#matrix_wrapper_fields
|
||||
#vec_wrapper_fields
|
||||
|
||||
#custom_fields
|
||||
|
||||
#new_fn_idents
|
||||
|
||||
builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
|
||||
Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone())))
|
||||
});
|
||||
|
||||
builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
|
||||
Ok(crate::ScriptBorrow::from_component::<#path>(None))
|
||||
});
|
||||
|
||||
#meta_method_idents
|
||||
|
||||
#matrix_wrapper_methods
|
||||
#vec_wrapper_methods
|
||||
|
||||
#custom_methods
|
||||
#new_func_tokens
|
||||
#meta_methods_tokens
|
||||
#extra_builds
|
||||
}
|
||||
}
|
||||
|
||||
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
let t = std::any::TypeId::of::<#path>();
|
||||
println!("Got id of {}, it is {:?}", stringify!(#path), t);
|
||||
t
|
||||
std::any::TypeId::of::<#path>()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
use quote::quote;
|
||||
use syn::{Path, Ident};
|
||||
use proc_macro2::Span;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct VecWrapper;
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl VecWrapper {
|
||||
fn vec_size(&self, wrapper_ident: &Ident) -> usize {
|
||||
let name = wrapper_ident.to_string();
|
||||
name[name.len() - 1..].parse::<usize>()
|
||||
.or_else(|_| name[name.len() - 2.. name.len() - 1].parse::<usize>())
|
||||
.expect("Failure to grab Vec size from ident name")
|
||||
}
|
||||
|
||||
/// Returns the token stream of the type of the axis of the vec (Vec2 vs IVec2 vs I64Vec2, etc.)
|
||||
fn vec_axis_type(&self, wrapper_ident: &Ident) -> &'static str {
|
||||
let name = wrapper_ident.to_string();
|
||||
let start = name.find("Vec").unwrap();
|
||||
|
||||
let before = &name[start - 1.. start];
|
||||
match before {
|
||||
"D" => return "f64",
|
||||
"I" => return "i32",
|
||||
"U" => return "u32",
|
||||
"B" => return "bool",
|
||||
_ => {},
|
||||
}
|
||||
//println!("before is {before}");
|
||||
|
||||
let three_before = &name[start - 3.. start];
|
||||
match three_before {
|
||||
"I64" => return "i64",
|
||||
"U64" => return "u64",
|
||||
_ => {},
|
||||
}
|
||||
//println!("three before is {three_before}");
|
||||
|
||||
"f32"
|
||||
}
|
||||
|
||||
pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
|
||||
let mut consts = vec![quote!(ZERO), quote!(ONE), quote!(X),
|
||||
quote!(Y), ]; // , quote!(AXES)
|
||||
|
||||
let vec_size = self.vec_size(wrapper_ident);
|
||||
let axis_type_name = self.vec_axis_type(wrapper_ident);
|
||||
|
||||
if axis_type_name.contains("b") {
|
||||
return quote! {
|
||||
builder.field("FALSE", #wrapper_ident(#wrapped_path::FALSE));
|
||||
builder.field("TRUE", #wrapper_ident(#wrapped_path::TRUE));
|
||||
};
|
||||
}
|
||||
|
||||
if vec_size >= 3 {
|
||||
consts.push(quote!(Z));
|
||||
|
||||
// no negative numbers for unsigned vecs
|
||||
if !axis_type_name.contains("u") {
|
||||
consts.push(quote!(NEG_Z));
|
||||
}
|
||||
}
|
||||
|
||||
if vec_size == 4 {
|
||||
consts.push(quote!(W));
|
||||
|
||||
// no negative numbers for unsigned vecs
|
||||
if !axis_type_name.contains("u") {
|
||||
consts.push(quote!(NEG_W));
|
||||
}
|
||||
}
|
||||
|
||||
// no negative numbers for unsigned vecs
|
||||
if !axis_type_name.contains("u") {
|
||||
consts.push(quote!(NEG_X));
|
||||
consts.push(quote!(NEG_Y));
|
||||
consts.push(quote!(NEG_ONE));
|
||||
}
|
||||
|
||||
if axis_type_name.contains("f") {
|
||||
consts.push(quote!(NAN))
|
||||
}
|
||||
|
||||
let const_tokens = consts.iter().map(|cnst| {
|
||||
let const_name = cnst.to_string();
|
||||
|
||||
quote! {
|
||||
builder.field(#const_name, #wrapper_ident(#wrapped_path::#cnst));
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#(#const_tokens)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_method_tokens(&self, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
|
||||
let vec_size = self.vec_size(wrapper_ident);
|
||||
let axis_type_name = self.vec_axis_type(wrapper_ident);
|
||||
// methods that only some vecs have
|
||||
let mut optional_methods = vec![];
|
||||
|
||||
// boolean vectors dont have much :(
|
||||
if axis_type_name.contains("b") {
|
||||
return quote!(); // TODO: all, any, bitmask, splat
|
||||
}
|
||||
|
||||
if axis_type_name.contains("f") {
|
||||
let type_id = Ident::new(axis_type_name, Span::call_site());
|
||||
|
||||
optional_methods.push(
|
||||
quote! {
|
||||
builder.method("clamp_length",
|
||||
|_, this, (min, max): (#type_id, #type_id)| {
|
||||
Ok(#wrapper_ident(this.clamp_length(min, max)))
|
||||
});
|
||||
|
||||
builder.method("abs_diff_eq",
|
||||
|_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| {
|
||||
Ok(this.abs_diff_eq(rhs.0, max_abs_diff))
|
||||
});
|
||||
|
||||
builder.method("ceil",
|
||||
|_, this, (): ()| {
|
||||
Ok(#wrapper_ident(this.ceil()))
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if vec_size != 4 {
|
||||
optional_methods.push(
|
||||
quote! {
|
||||
builder.method("angle_between",
|
||||
|_, this, (rhs,): (#wrapper_ident,)| {
|
||||
Ok(this.angle_between(rhs.0))
|
||||
});
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !axis_type_name.contains("u") {
|
||||
optional_methods.push(
|
||||
quote! {
|
||||
builder.method("abs",
|
||||
|_, this, (): ()| {
|
||||
Ok(#wrapper_ident(this.abs()))
|
||||
});
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let optional_methods_iter = optional_methods.iter();
|
||||
quote! {
|
||||
|
||||
|
||||
builder.method("clamp",
|
||||
|_, this, (min, max): (#wrapper_ident, #wrapper_ident)| {
|
||||
Ok(#wrapper_ident(this.clamp(min.0, max.0)))
|
||||
});
|
||||
|
||||
// TODO: Not all Vecs have this
|
||||
/* builder.method("clamp_length",
|
||||
|_, this, (min, max): (f32, f32)| {
|
||||
Ok(#wrapper_ident(this.clamp_length(min, max)))
|
||||
}); */
|
||||
|
||||
|
||||
builder.method("to_array",
|
||||
|_, this, (): ()| {
|
||||
Ok(this.to_array())
|
||||
});
|
||||
|
||||
#(#optional_methods_iter)*
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
use std::{any::TypeId, ops::Deref};
|
||||
use elua::{AnyUserdata, AsLua, FromLua, State, Value};
|
||||
use lyra_resource::{gltf::Gltf, ResHandle, UntypedResHandle};
|
||||
use lyra_resource::{gltf::{Gltf, Material, Mesh}, Texture, ResHandle, UntypedResHandle};
|
||||
use lyra_game::scene::SceneGraph;
|
||||
use lyra_reflect::{Reflect, TypeData};
|
||||
use lyra_scripting_derive::lua_wrap_handle;
|
||||
use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct};
|
||||
|
||||
use crate::{lua::{LuaWrapper, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, lyra_engine, ScriptBorrow};
|
||||
|
||||
use crate as lyra_scripting;
|
||||
|
||||
pub struct LuaResHandleToComponent {
|
||||
/// Create the userdata component that
|
||||
pub fn_to_lua: fn(&State, UntypedResHandle) -> Option<AnyUserdata>,
|
||||
|
@ -109,7 +111,91 @@ impl<'a> FromLua<'a> for LuaResHandle {
|
|||
}
|
||||
}
|
||||
|
||||
wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness, skip(lua_reflect)); // doesn't need internal lua reflection methods
|
||||
wrap_lua_struct!(lyra_resource::gltf::Specular, skip(lua_reflect)); // doesn't need internal lua reflection methods
|
||||
|
||||
lua_wrap_handle!(SceneGraph, name=Scene, {});
|
||||
|
||||
lua_wrap_handle!(Mesh,
|
||||
field_getters={
|
||||
(material, {
|
||||
data.material.clone()
|
||||
.map(|v| LuaMaterialHandle(v.clone()))
|
||||
.as_lua(lua)
|
||||
})
|
||||
},
|
||||
{
|
||||
builder.method("indices", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
match &data.indices {
|
||||
Some(lyra_resource::gltf::MeshIndices::U16(v)) => {
|
||||
for (i, ind) in v.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_seti(i, *ind)?;
|
||||
}
|
||||
},
|
||||
Some(lyra_resource::gltf::MeshIndices::U32(v)) => {
|
||||
for (i, ind) in v.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_seti(i, *ind)?;
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
|
||||
Ok(Value::Table(table))
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: attributes
|
||||
}
|
||||
);
|
||||
lua_wrap_handle!(Texture, {});
|
||||
|
||||
lua_wrap_handle!(Material,
|
||||
field_getters={
|
||||
shader_uuid,
|
||||
name,
|
||||
double_sided,
|
||||
(base_color, wrapper=crate::lua::wrappers::LuaVec4),
|
||||
metallic,
|
||||
roughness,
|
||||
(base_color_texture, {
|
||||
data.base_color_texture.clone()
|
||||
.map(|v| LuaTextureHandle(v.clone()))
|
||||
.as_lua(lua)
|
||||
}),
|
||||
(metallic_roughness_texture, {
|
||||
data.metallic_roughness_texture.clone()
|
||||
.map(|v| LuaTextureHandle(v.clone()))
|
||||
.as_lua(lua)
|
||||
}),
|
||||
(pbr_glossiness, {
|
||||
data.pbr_glossiness.clone()
|
||||
.map(|v| LuaPbrGlossiness(v.clone()))
|
||||
.as_lua(lua)
|
||||
}),
|
||||
alpha_cutoff,
|
||||
(alpha_mode, {
|
||||
match data.alpha_mode {
|
||||
// TODO: Lua enums
|
||||
lyra_resource::gltf::AlphaMode::Opaque => "opaque",
|
||||
lyra_resource::gltf::AlphaMode::Mask => "mask",
|
||||
lyra_resource::gltf::AlphaMode::Blend => "blend",
|
||||
}.as_lua(lua)
|
||||
}),
|
||||
(specular, {
|
||||
data.specular.clone()
|
||||
.map(|v| LuaSpecular(v.clone()))
|
||||
.as_lua(lua)
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
lua_wrap_handle!(Gltf, {
|
||||
builder.method("scenes", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
|
@ -120,6 +206,32 @@ lua_wrap_handle!(Gltf, {
|
|||
table.raw_seti(i, LuaSceneHandle(scene.clone()))?;
|
||||
}
|
||||
|
||||
Ok(Value::Table(table))
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}).method("materials", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
for (i, mat) in data.materials.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_seti(i, LuaMaterialHandle(mat.clone()))?;
|
||||
}
|
||||
|
||||
Ok(Value::Table(table))
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}).method("meshes", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
for (i, mesh) in data.meshes.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_seti(i, LuaMeshHandle(mesh.clone()))?;
|
||||
}
|
||||
|
||||
Ok(Value::Table(table))
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
|
|
|
@ -37,4 +37,4 @@ impl LuaWrapper for LuaDeltaTime {
|
|||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
TypeId::of::<DeltaTime>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use lyra_scripting_derive::{lua_vec_wrap_extension, wrap_lua_struct};
|
||||
use crate::lyra_engine;
|
||||
use lyra_game::math;
|
||||
use lyra_scripting_derive::wrap_math_vec_copy;
|
||||
|
||||
use crate as lyra_scripting;
|
||||
|
||||
// f32 types
|
||||
wrap_math_vec_copy!(
|
||||
wrap_lua_struct!(
|
||||
math::Vec2,
|
||||
derives(PartialEq),
|
||||
derives(PartialEq, Copy),
|
||||
new,
|
||||
fields(x, y),
|
||||
metamethods(
|
||||
Add(LuaVec2, f32),
|
||||
|
@ -19,7 +17,9 @@ wrap_math_vec_copy!(
|
|||
Mod(LuaVec2, f32),
|
||||
Eq, Unm, ToString
|
||||
),
|
||||
custom_methods {
|
||||
{
|
||||
lua_vec_wrap_extension!(math::Vec2, LuaVec2);
|
||||
|
||||
builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| {
|
||||
let vals_clone = vals.clone();
|
||||
if let Some((x, y)) = vals.try_into_vals::<(f32, f32)>(lua)? {
|
||||
|
@ -33,9 +33,11 @@ wrap_math_vec_copy!(
|
|||
});
|
||||
}
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
|
||||
wrap_lua_struct!(
|
||||
math::Vec3,
|
||||
derives(PartialEq),
|
||||
derives(PartialEq, Copy),
|
||||
new,
|
||||
fields(x, y, z),
|
||||
metamethods(
|
||||
Add(LuaVec3, f32),
|
||||
|
@ -45,7 +47,9 @@ wrap_math_vec_copy!(
|
|||
Mod(LuaVec3, f32),
|
||||
Eq, Unm, ToString
|
||||
),
|
||||
custom_methods {
|
||||
{
|
||||
lua_vec_wrap_extension!(math::Vec3, LuaVec3);
|
||||
|
||||
builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| {
|
||||
let vals_clone = vals.clone();
|
||||
if let Some((x, y, z)) = vals.try_into_vals::<(f32, f32, f32)>(lua)? {
|
||||
|
@ -61,412 +65,38 @@ wrap_math_vec_copy!(
|
|||
}
|
||||
);
|
||||
|
||||
wrap_math_vec_copy!(
|
||||
wrap_lua_struct!(
|
||||
math::Vec4,
|
||||
derives(PartialEq),
|
||||
fields(w, x, y, z),
|
||||
derives(PartialEq, Copy),
|
||||
new,
|
||||
fields(x, y, z, w),
|
||||
metamethods(
|
||||
Add(LuaVec4, f32),
|
||||
Sub(LuaVec4, f32),
|
||||
Div(LuaVec4, f32),
|
||||
Mul(LuaVec4, f32),
|
||||
Mod(LuaVec4, f32),
|
||||
Eq, Unm
|
||||
)
|
||||
Eq, Unm, ToString
|
||||
),
|
||||
{
|
||||
lua_vec_wrap_extension!(math::Vec4, LuaVec4);
|
||||
}
|
||||
);
|
||||
|
||||
// =================================================
|
||||
|
||||
|
||||
/* wrap_math_vec_copy!(
|
||||
math::Vec3A,
|
||||
derives(PartialEq),
|
||||
fields(x, y, z),
|
||||
metamethods(
|
||||
Add(LuaVec3A, f32),
|
||||
Sub(LuaVec3A, f32),
|
||||
Div(LuaVec3A, f32),
|
||||
Mul(LuaVec3A, f32),
|
||||
Mod(LuaVec3A, f32),
|
||||
Eq, Unm
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// f64 types
|
||||
wrap_math_vec_copy!(
|
||||
math::DVec2,
|
||||
derives(PartialEq),
|
||||
fields(x, y),
|
||||
metamethods(
|
||||
Add(LuaDVec2, f64),
|
||||
Sub(LuaDVec2, f64),
|
||||
Div(LuaDVec2, f64),
|
||||
Mul(LuaDVec2, f64),
|
||||
Mod(LuaDVec2, f64),
|
||||
Eq, Unm
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::DVec3,
|
||||
derives(PartialEq),
|
||||
fields(x, y, z),
|
||||
metamethods(
|
||||
Add(LuaDVec3, f64),
|
||||
Sub(LuaDVec3, f64),
|
||||
Div(LuaDVec3, f64),
|
||||
Mul(LuaDVec3, f64),
|
||||
Mod(LuaDVec3, f64),
|
||||
Eq, Unm
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::DVec4,
|
||||
derives(PartialEq),
|
||||
fields(w, x, y, z),
|
||||
metamethods(
|
||||
Add(LuaDVec4, f64),
|
||||
Sub(LuaDVec4, f64),
|
||||
Div(LuaDVec4, f64),
|
||||
Mul(LuaDVec4, f64),
|
||||
Mod(LuaDVec4, f64),
|
||||
Eq, Unm
|
||||
)
|
||||
);
|
||||
|
||||
// i32 types
|
||||
wrap_math_vec_copy!(
|
||||
math::IVec2,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y),
|
||||
metamethods(
|
||||
Add(LuaIVec2, i32),
|
||||
Sub(LuaIVec2, i32),
|
||||
Div(LuaIVec2, i32),
|
||||
Mul(LuaIVec2, i32),
|
||||
Mod(LuaIVec2, i32),
|
||||
Shl(LuaIVec2, LuaUVec2, i32),
|
||||
Shr(LuaIVec2, LuaUVec2, i32),
|
||||
BAnd(LuaIVec2, i32),
|
||||
BOr(LuaIVec2, i32),
|
||||
BXor(LuaIVec2, i32),
|
||||
Eq, Unm, BNot
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::IVec3,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y, z),
|
||||
metamethods(
|
||||
Add(LuaIVec3, i32),
|
||||
Sub(LuaIVec3, i32),
|
||||
Div(LuaIVec3, i32),
|
||||
Mul(LuaIVec3, i32),
|
||||
Mod(LuaIVec3, i32),
|
||||
Shl(LuaIVec3, LuaUVec3, i32),
|
||||
Shr(LuaIVec3, LuaUVec3, i32),
|
||||
BAnd(LuaIVec3, i32),
|
||||
BOr(LuaIVec3, i32),
|
||||
BXor(LuaIVec3, i32),
|
||||
Eq, Unm, BNot
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::IVec4,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(w, x, y, z),
|
||||
metamethods(
|
||||
Add(LuaIVec4, i32),
|
||||
Sub(LuaIVec4, i32),
|
||||
Div(LuaIVec4, i32),
|
||||
Mul(LuaIVec4, i32),
|
||||
Mod(LuaIVec4, i32),
|
||||
Shl(LuaIVec4, LuaUVec4, i32),
|
||||
Shr(LuaIVec4, LuaUVec4, i32),
|
||||
BAnd(LuaIVec4, i32),
|
||||
BOr(LuaIVec4, i32),
|
||||
BXor(LuaIVec4, i32),
|
||||
Eq, Unm, BNot
|
||||
)
|
||||
);
|
||||
|
||||
// u32 types
|
||||
wrap_math_vec_copy!(
|
||||
math::UVec2,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y),
|
||||
metamethods(
|
||||
Add(LuaUVec2, u32),
|
||||
Sub(LuaUVec2, u32),
|
||||
Div(LuaUVec2, u32),
|
||||
Mul(LuaUVec2, u32),
|
||||
Mod(LuaUVec2, u32),
|
||||
Shl(LuaUVec2, LuaIVec2, i32),
|
||||
Shr(LuaUVec2, LuaIVec2, i32),
|
||||
BAnd(LuaUVec2, u32),
|
||||
BOr(LuaUVec2, u32),
|
||||
BXor(LuaUVec2, u32),
|
||||
Eq, BNot
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::UVec3,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y, z),
|
||||
metamethods(
|
||||
Add(LuaUVec3, u32),
|
||||
Sub(LuaUVec3, u32),
|
||||
Div(LuaUVec3, u32),
|
||||
Mul(LuaUVec3, u32),
|
||||
Mod(LuaUVec3, u32),
|
||||
Shl(LuaUVec3, LuaIVec3, i32),
|
||||
Shr(LuaUVec3, LuaIVec3, i32),
|
||||
BAnd(LuaUVec3, u32),
|
||||
BOr(LuaUVec3, u32),
|
||||
BXor(LuaUVec3, u32),
|
||||
Eq, BNot
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::UVec4,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(w, x, y, z),
|
||||
metamethods(
|
||||
Add(LuaUVec4, u32),
|
||||
Sub(LuaUVec4, u32),
|
||||
Div(LuaUVec4, u32),
|
||||
Mul(LuaUVec4, u32),
|
||||
Mod(LuaUVec4, u32),
|
||||
Shl(LuaUVec4, LuaIVec4, i32),
|
||||
Shr(LuaUVec4, LuaIVec4, i32),
|
||||
BAnd(LuaUVec4, u32),
|
||||
BOr(LuaUVec4, u32),
|
||||
BXor(LuaUVec4, u32),
|
||||
Eq, BNot
|
||||
)
|
||||
);
|
||||
|
||||
// i64 types
|
||||
wrap_math_vec_copy!(
|
||||
math::I64Vec2,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y),
|
||||
metamethods(
|
||||
Add(LuaI64Vec2, i64),
|
||||
Sub(LuaI64Vec2, i64),
|
||||
Div(LuaI64Vec2, i64),
|
||||
Mul(LuaI64Vec2, i64),
|
||||
Mod(LuaI64Vec2, i64),
|
||||
Shl(i64),
|
||||
Shr(i64),
|
||||
BAnd(LuaI64Vec2, i64),
|
||||
BOr(LuaI64Vec2, i64),
|
||||
BXor(LuaI64Vec2, i64),
|
||||
Eq, BNot
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::I64Vec3,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y, z),
|
||||
metamethods(
|
||||
Add(LuaI64Vec3, i64),
|
||||
Sub(LuaI64Vec3, i64),
|
||||
Div(LuaI64Vec3, i64),
|
||||
Mul(LuaI64Vec3, i64),
|
||||
Mod(LuaI64Vec3, i64),
|
||||
Shl(i64),
|
||||
Shr(i64),
|
||||
BAnd(LuaI64Vec3, i64),
|
||||
BOr(LuaI64Vec3, i64),
|
||||
BXor(LuaI64Vec3, i64),
|
||||
Eq, BNot
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::I64Vec4,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(w, x, y, z),
|
||||
metamethods(
|
||||
Add(LuaI64Vec4, i64),
|
||||
Sub(LuaI64Vec4, i64),
|
||||
Div(LuaI64Vec4, i64),
|
||||
Mul(LuaI64Vec4, i64),
|
||||
Mod(LuaI64Vec4, i64),
|
||||
Shl(i64),
|
||||
Shr(i64),
|
||||
BAnd(LuaI64Vec4, i64),
|
||||
BOr(LuaI64Vec4, i64),
|
||||
BXor(LuaI64Vec4, i64),
|
||||
Eq, BNot
|
||||
)
|
||||
);
|
||||
|
||||
// u64 types
|
||||
wrap_math_vec_copy!(
|
||||
math::U64Vec2,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y),
|
||||
metamethods(
|
||||
Add(LuaU64Vec2, u64),
|
||||
Sub(LuaU64Vec2, u64),
|
||||
Div(LuaU64Vec2, u64),
|
||||
Mul(LuaU64Vec2, u64),
|
||||
Mod(LuaU64Vec2, u64),
|
||||
Shl(i64),
|
||||
Shr(i64),
|
||||
BAnd(LuaU64Vec2, u64),
|
||||
BOr(LuaU64Vec2, u64),
|
||||
BXor(LuaU64Vec2, u64),
|
||||
Eq, BNot
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::U64Vec3,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y, z),
|
||||
metamethods(
|
||||
Add(LuaU64Vec3, u64),
|
||||
Sub(LuaU64Vec3, u64),
|
||||
Div(LuaU64Vec3, u64),
|
||||
Mul(LuaU64Vec3, u64),
|
||||
Mod(LuaU64Vec3, u64),
|
||||
Shl(i64),
|
||||
Shr(i64),
|
||||
BAnd(LuaU64Vec3, u64),
|
||||
BOr(LuaU64Vec3, u64),
|
||||
BXor(LuaU64Vec3, u64),
|
||||
Eq, BNot
|
||||
)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::U64Vec4,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(w, x, y, z),
|
||||
metamethods(
|
||||
Add(LuaU64Vec4, u64),
|
||||
Sub(LuaU64Vec4, u64),
|
||||
Div(LuaU64Vec4, u64),
|
||||
Mul(LuaU64Vec4, u64),
|
||||
Mod(LuaU64Vec4, u64),
|
||||
Shl(i64),
|
||||
Shr(i64),
|
||||
BAnd(LuaU64Vec4, u64),
|
||||
BOr(LuaU64Vec4, u64),
|
||||
BXor(LuaU64Vec4, u64),
|
||||
Eq, BNot
|
||||
)
|
||||
);*/
|
||||
|
||||
// bool types
|
||||
/* wrap_math_vec_copy!(
|
||||
math::BVec2,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y),
|
||||
metamethods(Eq, BAnd, BOr, BXOr, BNot)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::BVec3,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(x, y, z),
|
||||
metamethods(Eq, BAnd, BOr, BXOr, BNot)
|
||||
);
|
||||
wrap_math_vec_copy!(
|
||||
math::BVec4,
|
||||
derives(PartialEq, Eq, Hash),
|
||||
fields(w, x, y, z),
|
||||
metamethods(Eq, BAnd, BOr, BXOr, BNot)
|
||||
); */
|
||||
|
||||
// mat2
|
||||
/* wrap_math_vec_copy!(
|
||||
math::Mat2,
|
||||
derives(PartialEq),
|
||||
no_new,
|
||||
matrix {
|
||||
col_type = LuaVec2
|
||||
},
|
||||
metamethods(
|
||||
Eq,
|
||||
Add,
|
||||
Sub,
|
||||
Mul(LuaMat2, f32),
|
||||
Unm
|
||||
)
|
||||
); */
|
||||
|
||||
// TODO
|
||||
/* wrap_math_vec_copy!(
|
||||
math::Mat4,
|
||||
derives(PartialEq),
|
||||
no_new,
|
||||
matrix {
|
||||
col_type = LuaVec4
|
||||
},
|
||||
metamethods(
|
||||
Eq,
|
||||
Add,
|
||||
Sub,
|
||||
Mul(LuaMat4, f32),
|
||||
Unm
|
||||
)
|
||||
); */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================================================
|
||||
|
||||
|
||||
|
||||
/// A macro that generates field getters and setters for lua wrapped types.
|
||||
macro_rules! wrapped_field_getsetters {
|
||||
($builder: ident, $name: literal, $field: ident, $type: ident) => {
|
||||
$builder.field_getter($name, |_, this| {
|
||||
Ok($type(this.$field))
|
||||
});
|
||||
$builder.field_setter($name, |_, this, v: $type| {
|
||||
this.$field = *v;
|
||||
Ok(())
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/// A macro that generates field getters and setters for types that already implement As/FromLua.
|
||||
macro_rules! field_getsetters {
|
||||
($builder: ident, $name: literal, $field: ident, $type: ty) => {
|
||||
$builder.field_getter($name, |_, this| {
|
||||
Ok(this.$field)
|
||||
});
|
||||
$builder.field_setter($name, |_, this, v: $type| {
|
||||
this.$field = v;
|
||||
Ok(())
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
wrap_math_vec_copy!(
|
||||
wrap_lua_struct!(
|
||||
math::Quat,
|
||||
derives(PartialEq),
|
||||
no_new,
|
||||
derives(PartialEq, Copy),
|
||||
//new,
|
||||
fields(x, y, z, w),
|
||||
metamethods(
|
||||
Eq,
|
||||
// __mul for LuaVec3 is manually implemented below since it doesn't return Self
|
||||
//Mul(LuaQuat, f32),
|
||||
Add,
|
||||
Sub,
|
||||
Div(f32),
|
||||
// __mul for LuaVec3 is manually implemented below since it doesn't return Self
|
||||
//Mul(LuaQuat, f32),
|
||||
),
|
||||
custom_fields {
|
||||
field_getsetters!(builder, "x", x, f32);
|
||||
field_getsetters!(builder, "y", y, f32);
|
||||
field_getsetters!(builder, "z", z, f32);
|
||||
field_getsetters!(builder, "w", w, f32);
|
||||
},
|
||||
custom_methods {
|
||||
{
|
||||
// manually implemented since Quat doesn't have a `new` function
|
||||
builder.function("new", |_, (x, y, z, w)| {
|
||||
Ok(Self(math::Quat::from_xyzw(x, y, z, w)))
|
||||
|
@ -545,17 +175,11 @@ wrap_math_vec_copy!(
|
|||
}
|
||||
);
|
||||
|
||||
wrap_math_vec_copy!(
|
||||
wrap_lua_struct!(
|
||||
math::Transform,
|
||||
derives(PartialEq),
|
||||
no_new,
|
||||
derives(PartialEq, Copy),
|
||||
metamethods(ToString, Eq),
|
||||
custom_fields {
|
||||
wrapped_field_getsetters!(builder, "translation", translation, LuaVec3);
|
||||
wrapped_field_getsetters!(builder, "rotation", rotation, LuaQuat);
|
||||
wrapped_field_getsetters!(builder, "scale", scale, LuaVec3);
|
||||
},
|
||||
custom_methods {
|
||||
{
|
||||
builder.function("default", |_, ()| {
|
||||
Ok(Self(math::Transform::default()))
|
||||
});
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
pub mod math;
|
||||
mod math;
|
||||
pub use math::*;
|
||||
|
||||
pub mod delta_time;
|
||||
pub use delta_time::*;
|
||||
|
||||
pub mod input_actions;
|
||||
mod input_actions;
|
||||
pub use input_actions::*;
|
||||
|
||||
pub mod asset;
|
||||
pub use asset::*;
|
||||
mod asset;
|
||||
pub use asset::*;
|
||||
|
||||
mod delta_time;
|
||||
pub use delta_time::*;
|
|
@ -1,69 +0,0 @@
|
|||
use std::any::TypeId;
|
||||
use std::{cell::Ref, sync::Arc};
|
||||
|
||||
use elua::FromLua;
|
||||
use lyra_game::scene::ModelComponent;
|
||||
use lyra_reflect::Reflect;
|
||||
use lyra_resource::{Model, ResHandle};
|
||||
|
||||
use crate::lua::LuaWrapper;
|
||||
use crate::lyra_engine;
|
||||
use crate::{lua::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow};
|
||||
|
||||
use super::LuaResHandle;
|
||||
|
||||
#[derive(Clone, Reflect)]
|
||||
pub struct LuaModelComponent(pub ModelComponent);
|
||||
|
||||
impl elua::Userdata for LuaModelComponent {
|
||||
fn name() -> String {
|
||||
"ModelComponent".to_string()
|
||||
}
|
||||
|
||||
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
|
||||
builder
|
||||
.function("new", |_, model: Ref<LuaResHandle>| {
|
||||
let res = model.0.clone();
|
||||
let res_any = res.as_arc_any();
|
||||
match res_any.downcast::<ResHandle<Model>>() {
|
||||
Ok(handle) => {
|
||||
let res = ResHandle::<Model>::clone(&handle);
|
||||
Ok(Self(ModelComponent(res)))
|
||||
},
|
||||
Err(_) => {
|
||||
Err(elua::Error::BadArgument {
|
||||
func: Some("ModelComponent:new".to_string()),
|
||||
arg_index: 1,
|
||||
arg_name: Some("model".to_string()),
|
||||
error: Arc::new(
|
||||
elua::Error::runtime("resource handle is not a handle to a Model")
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
|
||||
Ok(ScriptBorrow::from_component::<ModelComponent>(None))
|
||||
})
|
||||
.method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
|
||||
Ok(ScriptBorrow::from_component(Some(this.0.clone())))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromLua<'a> for LuaModelComponent {
|
||||
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
|
||||
let tyname = val.type_name();
|
||||
let ud = val.as_userdata()
|
||||
.ok_or(elua::Error::type_mismatch("Model", &tyname))?;
|
||||
let ud = ud.as_ref::<LuaModelComponent>()?;
|
||||
|
||||
Ok(ud.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaWrapper for LuaModelComponent {
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
TypeId::of::<ModelComponent>()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue