Add optional support for teal

This commit is contained in:
SeanOMik 2024-02-28 18:39:15 -05:00
parent 936ea60634
commit 4c1a1588c5
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
16 changed files with 935 additions and 17 deletions

4
Cargo.toml Executable file → Normal file
View File

@ -8,3 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
mlua-sys = { version = "0.5.0", features = ["lua54"] } mlua-sys = { version = "0.5.0", features = ["lua54"] }
thiserror = "1.0.56" thiserror = "1.0.56"
elua-derive = { path = "./elua-derive", optional = true}
[features]
teal = ["elua-derive"]

18
elua-derive/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "elua-derive"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bytes = "1.5.0"
proc-macro2 = "1.0.78"
quote = "1.0.35"
reqwest = { version = "0.11.24", features = ["blocking"] }
syn = "2.0.51"
tempdir = "0.3.7"
zip = "0.6.6"

114
elua-derive/src/lib.rs Normal file
View File

@ -0,0 +1,114 @@
use std::{fs::{self, File}, io::Read};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parenthesized, parse_macro_input, token, Ident, LitStr};
use tempdir::TempDir;
const DEFAULT_TEAL_VERSION: &str = "0.15.3";
enum CompilerSource {
Github,
Path,
}
impl syn::parse::Parse for CompilerSource {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ident: Ident = input.parse()?;
let ident_str = ident.to_string().to_lowercase();
let ident_str = ident_str.as_str();
match ident_str {
"github" => Ok(CompilerSource::Github),
"path" => Ok(CompilerSource::Path),
_ => Err(syn::Error::new_spanned(ident, "unknown teal download source"))
}
}
}
struct EmbedInput {
source: CompilerSource,
path: String,
}
impl syn::parse::Parse for EmbedInput {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let src: CompilerSource = input.parse()?;
let path = if input.peek(token::Paren) {
let content;
let _parens: token::Paren = parenthesized!(content in input);
let path: LitStr = content.parse()?;
path.value()
} else {
DEFAULT_TEAL_VERSION.to_string()
};
Ok(EmbedInput {
source: src,
path,
})
}
}
#[proc_macro]
pub fn embed_compiler(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as EmbedInput);
let tl_lua = match input.source {
CompilerSource::Github => {
let path = if input.path.starts_with("v") {
input.path[1..].to_string()
} else { input.path };
download_from_github(path)
},
CompilerSource::Path => {
fs::read_to_string(input.path)
.expect("Failed to read tl.lua file from provided path")
},
};
let tl_loader_str = format!(
"local tl = (function()\n{}\nend)()\ntl.loader()\n",
tl_lua
);
let teal_compiler = quote! {
|require: &str| {
format!("{}\n return require '{}' ", #tl_loader_str, require)
}
};
teal_compiler.into()
}
fn download_teal(url: String, inner_folder: String) -> String {
let resp = reqwest::blocking::get(url).expect("failed to download teal from url");
let bytes = resp.bytes().expect("failed to get bytes from request");
let dir = TempDir::new("tl").expect("failed to create tempdir");
let dir = dir.path().join("tl.tar.gz");
fs::write(&dir, bytes).expect("failed to write teal zip file to tempdir");
let file = File::open(dir).expect("Unable to open teal zip file!");
let mut arch = zip::ZipArchive::new(&file)
.expect("failure to read teal zip file");
let mut teal_lua = arch.by_name(&format!("{}/tl.lua", inner_folder))
.expect("failed to find tl.lua file inside zip archive");
let mut contents = Vec::with_capacity(10000);
teal_lua.read_to_end(&mut contents).expect("Failed to read zip file into buffer");
String::from_utf8(contents).expect("Failure to retrieve tl.lua as utf8 string!")
}
fn download_from_github(version: String) -> String {
let url = format!("https://github.com/teal-language/tl/archive/v{}.zip", version);
let inner = format!("tl-{version}");
download_teal(url, inner)
}

View File

@ -0,0 +1,6 @@
use elua_derive::embed_compiler;
#[test]
fn from_github() {
let compiler = embed_compiler!(Github);
}

View File

@ -43,6 +43,11 @@ pub use table_iter::*;
pub mod table_pairs; pub mod table_pairs;
pub use table_pairs::*; pub use table_pairs::*;
#[cfg(feature = "teal")]
pub mod teal;
#[cfg(feature = "teal")]
pub use teal::*;
#[cfg(test)] #[cfg(test)]
pub mod tests; pub mod tests;

View File

@ -499,7 +499,7 @@ impl State {
pub(crate) fn create_userdata_metatable<'a, T: Userdata + 'static>(&'a self) -> Result<Table<'a>> { pub(crate) fn create_userdata_metatable<'a, T: Userdata + 'static>(&'a self) -> Result<Table<'a>> {
let mut builder = UserdataBuilder::<T>::new(); let mut builder = UserdataBuilder::<T>::new();
T::build(self, &mut builder)?; T::build(&mut builder);
let getters = builder.field_getters; let getters = builder.field_getters;
let setters = builder.field_setters; let setters = builder.field_setters;

69
src/teal/fn_desc.rs Normal file
View File

@ -0,0 +1,69 @@
use crate::{GenerateTealDefinition, Type};
#[derive(Clone)]
pub struct FuncArg {
pub name: Option<String>,
pub ty: Type
}
/// A struct that stores type information about a function
#[derive(Clone)]
pub struct FunctionDesc {
/// The name of the function
pub name: String,
/// A boolean indicating if this is a part of userdata
pub is_field: bool,
/// The arguments of the function
pub args: Vec<FuncArg>,
/// The return type of the function, `None` if it returns nothing
pub return_ty: Option<Type>,
}
impl GenerateTealDefinition for FunctionDesc {
fn generate(&self, ctx: &mut crate::GenerateContext) {
let args: String = self.args.iter()
.map(|t| match &t.name {
Some(name) => {
// include the argument name if one is set
format!("{}: {}", name, t.ty.name)
},
None => t.ty.name.clone()
})
.collect::<Vec<_>>()
.join(", ");
let ret = match &self.return_ty {
Some(ty) => format!(": {}", ty.name),
None => String::new()
};
let content = format!("function({args}){ret}");
if self.is_field {
ctx.write(&self.name)
.write(": ")
.write(&content)
.write_new_line();
} else {
ctx.write(&content);
}
}
}
/// Represents a Field on a Userdata.
#[derive(Clone)]
pub struct FieldDesc {
/// The name of the field
pub name: String,
/// The type of the field
pub ty: Type,
/// A boolean indicating if there is a setter for this field.
pub can_set: bool,
}
impl GenerateTealDefinition for FieldDesc {
fn generate(&self, ctx: &mut crate::GenerateContext) {
ctx.write(&self.name)
.write(": ")
.writeln(&self.ty.name);
}
}

197
src/teal/mod.rs Normal file
View File

@ -0,0 +1,197 @@
pub mod record;
pub use record::*;
pub mod fn_desc;
pub use fn_desc::*;
pub mod walker;
pub use walker::*;
pub mod userdata;
pub use userdata::*;
use crate::{Table, Value, Function, AnyUserdata, ValueVec};
#[derive(Default)]
pub struct GenerateContext {
/// The indentation level of current context.
pub level: u32,
/// The current record that things are being generated for.
pub record: Option<Type>,
pub content: String,
}
impl GenerateContext {
/// Writes `s` to the end of the current generating content.
///
/// If this write is happening on a new line, it will be indented
pub fn write(&mut self, s: &str) -> &mut Self {
if self.content.ends_with("\n") {
self.write_indent();
}
self.content = format!("{}{}", self.content, s);
self
}
/// Writes `s` directly to the end of the current generating content. There is no check for
/// indentation
pub fn write_raw(&mut self, s: &str) -> &mut Self {
if self.content.ends_with("\n") {
self.write_indent();
}
self.content = format!("{}{}", self.content, s);
self
}
/// Writes `s` followed by a new line directly to the end of the current generating content.
///
/// If this write is happening on a new line, it will be indented
pub fn writeln(&mut self, s: &str) -> &mut Self {
if self.content.ends_with("\n") {
self.write_indent();
}
self.content = format!("{}{}\n", self.content, s);
self
}
/// Writes a new line to the generating content.
pub fn write_new_line(&mut self) -> &mut Self {
self.content = format!("{}\n", self.content);
self
}
/// Writes the indent to the generating content that is valid for this level.
pub fn write_indent(&mut self) -> &mut Self {
let level = self.level * 4; // 4 spaces per indentation
let indent = " ".repeat(level as _);
self.content = format!("{}{}", self.content, indent);
self
}
/// Writes `s` on its own line, will insert a new line if its *not already on its own line*.
pub fn write_on_ln(&mut self, s: &str) -> &mut Self {
if self.content.ends_with("\n") {
self.write(s);
} else {
self.write_new_line()
.write(s);
}
self
}
/// Increases the indentation level by 1.
pub fn inc_level(&mut self) -> &mut Self {
self.level += 1;
self
}
/// Decreases the indentation level by 1.
pub fn dec_level(&mut self) -> &mut Self {
self.level -= 1;
self
}
}
pub trait GenerateTealDefinition {
fn generate(&self, ctx: &mut GenerateContext);
}
pub trait LuaTypeName {
fn get_lua_name() -> String;
}
// implements LuaTypeName for types that have the exact same name as the Rust type.
macro_rules! impl_type_name_simple {
($name: literal, $t: ident) => {
impl LuaTypeName for $t {
fn get_lua_name() -> String {
$name.to_string()
}
}
};
($name:literal, $t:ident <$( $generic: tt ),+>) => {
impl<$($generic: LuaTypeName,)+ > LuaTypeName for $t <$( $generic ),+ >{
fn get_lua_name() -> String {
let generic_name = {
let v = vec![$( $generic::get_lua_name(), )+];
v.join(", ")
};
format!("{}<{}>", $name, generic_name)
}
}
};
($lua_name: literal, $name:tt, $($lts:lifetime),+) => {
impl<$($lts,)+ > LuaTypeName for $name <$( $lts ),+ >{
fn get_lua_name() -> String {
$lua_name.to_string()
}
}
}
}
impl_type_name_simple!("Boolean", bool);
impl_type_name_simple!("Number", f32);
impl_type_name_simple!("Number", f64);
impl_type_name_simple!("Number", u8);
impl_type_name_simple!("Number", u16);
impl_type_name_simple!("Number", u32);
impl_type_name_simple!("Number", u64);
impl_type_name_simple!("Number", usize);
impl_type_name_simple!("Number", i8);
impl_type_name_simple!("Number", i16);
impl_type_name_simple!("Number", i32);
impl_type_name_simple!("Number", i64);
impl_type_name_simple!("Number", isize);
impl_type_name_simple!("string", String);
impl_type_name_simple!("T | nil", Option<T>);
impl_type_name_simple!("T", Table, 'a);
impl_type_name_simple!("any", Value, 'a);
impl_type_name_simple!("any", ValueVec, 'a);
impl_type_name_simple!("function(any)", Function, 'a);
impl_type_name_simple!("T", AnyUserdata, 'a);
impl LuaTypeName for () {
fn get_lua_name() -> String {
"()".to_string()
}
}
/// Tuple used to implement LuaTypeName for tuples that are used in function arguments
macro_rules! impl_type_name_tuples {
( $count: expr, $first: tt, $( $name: tt ),+ ) => {
impl<$first: LuaTypeName, $($name: LuaTypeName,)+> LuaTypeName for ($first, $($name),+) {
fn get_lua_name() -> String {
let tuple_names = {
let v = vec![$first::get_lua_name(), $( $name::get_lua_name(), )+];
v.join(", ")
};
format!("({})", tuple_names)
}
}
impl_type_name_tuples!(count - 1, $( $name ),+);
};
( $count: expr, $last: tt ) => {
impl<$last: LuaTypeName> LuaTypeName for ($last,) {
fn get_lua_name() -> String {
$last::get_lua_name()
}
}
};
}
impl_type_name_tuples! { 16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16 }

60
src/teal/record.rs Normal file
View File

@ -0,0 +1,60 @@
use std::ops::{Deref, DerefMut};
use crate::{FieldDesc, FunctionDesc, GenerateTealDefinition, LuaTypeName};
pub struct Record {
pub(crate) ty: Type,
pub fields: Vec<FieldDesc>,
pub functions: Vec<FunctionDesc>,
}
impl Deref for Record {
type Target = Type;
fn deref(&self) -> &Self::Target {
&self.ty
}
}
impl DerefMut for Record {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.ty
}
}
impl GenerateTealDefinition for Record {
fn generate(&self, ctx: &mut crate::GenerateContext) {
ctx.record = Some(self.ty.clone());
ctx.write("record ")
.writeln(&self.ty.name)
.inc_level();
for field in self.fields.iter() {
field.generate(ctx);
}
ctx.write_new_line();
for func in self.functions.iter() {
func.generate(ctx);
}
ctx.dec_level()
.write_on_ln("end");
}
}
/// A struct that only stores only the type of a Record; so the name, and generics.
#[derive(Clone)]
pub struct Type {
/// The name of the type, including generics, i.e., `LuaBuffer<T>`
pub name: String,
}
impl Type {
pub fn new<T: LuaTypeName>() -> Self {
Self {
name: T::get_lua_name(),
}
}
}

360
src/teal/userdata.rs Normal file
View File

@ -0,0 +1,360 @@
use std::{collections::HashMap, mem, ops::{Deref, DerefMut}};
use crate::{AsLua, FieldDesc, FromLua, FromLuaVec, FuncArg, FunctionDesc, LuaTypeName, State, Type, Userdata, UserdataBuilder};
enum MutCow<'a, T> {
Borrowed(&'a mut T),
Owned(T),
}
impl<'a, T> Deref for MutCow<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
MutCow::Borrowed(v) => v,
MutCow::Owned(v) => &v,
}
}
}
impl<'a, T> DerefMut for MutCow<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
MutCow::Borrowed(v) =>v,
MutCow::Owned(v) => v,
}
}
}
pub trait TealUserdata: Sized {
fn name() -> String;
fn build<'a>(builder: &mut TealUserdataBuilder<'a, Self>);
}
pub struct TealUserdataBuilder<'a, T>
where
// Userdata is implemented for all types that implement TealUserdata with the
// generic impl below.
T: Userdata
{
inner: MutCow<'a, UserdataBuilder<'a, T>>,
pub(crate) fields: HashMap<String, FieldDesc>,
pub(crate) funcs: HashMap<String, FunctionDesc>,
func_arg_names: Option<Vec<String>>,
}
impl<'a, T: Userdata> From<&'a mut UserdataBuilder<'a, T>> for TealUserdataBuilder<'a, T> {
fn from(value: &'a mut UserdataBuilder<'a, T>) -> Self {
Self {
inner: MutCow::Borrowed(value),
funcs: Default::default(),
fields: Default::default(),
func_arg_names: None,
}
}
}
pub(crate) fn function_desc<'a, A, R>(func_arg_names: &mut Option<Vec<String>>, fn_name: &str) -> FunctionDesc
where
A: FromLuaVec<'a> + LuaTypeName,
R: AsLua<'a> + LuaTypeName,
{
let arg_ty = A::get_lua_name();
let args: Vec<FuncArg> = if A::value_num().unwrap_or(0) > 1 {
// if the Args are multiple types, the type name was created as a tuple.
// to get each type of them, the surrounding parenthesis are removed,
// and then the string is split by the commas.
let args_split = arg_ty[1..arg_ty.len()-1]
.split(",");
if let Some(names) = func_arg_names.take() {
let args_num = A::value_num().unwrap_or(0);
debug_assert_eq!(names.len(), args_num,
"Invalid number of argument names was provided! (Expected {}, received {})",
args_num, names.len());
args_split.zip(names.into_iter())
.map(|(ty, name)| FuncArg {
name: Some(name.to_string()),
ty: Type {
name: ty.trim().to_string()
}
})
.collect()
} else {
args_split
.map(|s| FuncArg { name: None, ty: Type { name: s.trim().to_string() } })
.collect()
}
} else {
if &arg_ty == "()" {
todo!("Dont insert any arg");
} else {
if let Some(mut names) = func_arg_names.take() {
debug_assert_eq!(names.len(), 1,
"Invalid number of argument names was provided! (Expected {}, received {})",
1, names.len());
vec![
FuncArg {
name: names.pop(),
ty: Type {
name: arg_ty
}
}
]
} else {
vec![
FuncArg {
name: None,
ty: Type {
name: arg_ty
}
}
]
}
}
};
let ret_name = R::get_lua_name();
let ret_ty = match ret_name.as_str() {
"()" => None,
_ => Some(Type { name: ret_name })
};
let func_desc = FunctionDesc {
name: fn_name.to_string(),
is_field: true,
args,
return_ty: ret_ty,
};
func_desc
}
impl<'a, T: Userdata> TealUserdataBuilder<'a, T> {
pub(crate) fn new() -> Self {
Self {
inner: MutCow::Owned(UserdataBuilder::new()),
fields: Default::default(),
funcs: Default::default(),
func_arg_names: None,
}
}
pub fn field_getter<F, R>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &T) -> crate::Result<R> + 'static,
R: AsLua<'a> + LuaTypeName,
T: TealUserdata + 'static
{
let tname = R::get_lua_name();
// maybe field_setter was ran first
if !self.fields.contains_key(&tname) {
let desc = FieldDesc {
name: name.to_string(),
ty: Type {
name: tname.clone(),
},
can_set: false,
};
self.fields.insert(tname, desc);
}
self.inner.field_getter(name, f);
self
}
pub fn field_setter<F, V>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &mut T, V) -> crate::Result<()> + 'static,
V: FromLua<'a> + LuaTypeName,
T: TealUserdata + 'static
{
let tname = V::get_lua_name();
if let Some(field) = self.fields.get_mut(&tname) {
field.can_set = true;
} else {
let desc = FieldDesc {
name: name.to_string(),
ty: Type {
name: tname.clone(),
},
can_set: true,
};
self.fields.insert(tname, desc);
}
self.inner.field_setter(name, f);
self
}
pub fn function<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a> + LuaTypeName,
R: AsLua<'a> + LuaTypeName,
{
let func_desc = function_desc::<A, R>(&mut self.func_arg_names, name);
self.funcs.insert(name.to_string(), func_desc);
self.inner.function(name, f);
self
}
pub fn method<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &T, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a> + LuaTypeName,
R: AsLua<'a> + LuaTypeName,
T: TealUserdata + 'static
{
let mut func_desc = function_desc::<A, R>(&mut self.func_arg_names, name);
let mut tmp = vec![FuncArg {
name: Some("self".to_string()),
ty: Type {
name: T::get_lua_name()
}
}];
// this is probably the fastest way to prepend to a Vec
tmp.append(&mut func_desc.args);
func_desc.args = tmp;
self.funcs.insert(name.to_string(), func_desc);
self.inner.method(name, f);
self
}
pub fn method_mut<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
where
F: Fn(&'a State, &mut T, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a> + LuaTypeName,
R: AsLua<'a> + LuaTypeName,
T: TealUserdata + 'static
{
let mut func_desc = function_desc::<A, R>(&mut self.func_arg_names, name);
let mut tmp = vec![FuncArg {
name: Some("self".to_string()),
ty: Type {
name: T::get_lua_name()
}
}];
// this is probably the fastest way to prepend to a Vec
tmp.append(&mut func_desc.args);
func_desc.args = tmp;
self.funcs.insert(name.to_string(), func_desc);
self.inner.method_mut(name, f);
self
}
pub fn meta_method<N, F, R, A>(&mut self, name: N, f: F) -> &mut Self
where
N: AsRef<str>,
F: Fn(&'a State, &T, A) -> crate::Result<R> + 'static,
A: FromLuaVec<'a> + LuaTypeName,
R: AsLua<'a> + LuaTypeName,
T: TealUserdata + 'static
{
let mut func_desc = function_desc::<A, R>(&mut self.func_arg_names, name.as_ref());
let mut tmp = vec![FuncArg {
name: Some("self".to_string()),
ty: Type {
name: T::get_lua_name()
}
}];
// this is probably the fastest way to prepend to a Vec
tmp.append(&mut func_desc.args);
func_desc.args = tmp;
self.funcs.insert(name.as_ref().to_string(), func_desc);
self.inner.meta_method(name, f);
self
}
pub fn set_arg_names(&mut self, names: &[&str]) -> &mut Self {
debug_assert!(self.func_arg_names.is_none(),
"Invalid attempt to set argument names for function. \
There are arguments that are currently pending to be set on a function");
self.func_arg_names = Some(names.iter().map(|n| n.to_string()).collect());
self
}
}
impl<T: TealUserdata> Userdata for T {
fn name() -> String {
<T as TealUserdata>::name()
}
fn build<'a>(builder: &mut UserdataBuilder<'a, Self>) {
let builder = unsafe { mem::transmute::<_, &mut UserdataBuilder<'_, Self>>(builder) };
let mut teal_build = TealUserdataBuilder::from(builder);
<T as TealUserdata>::build(&mut teal_build);
}
}
impl<T: TealUserdata> LuaTypeName for T {
fn get_lua_name() -> String {
T::name()
}
}
#[cfg(test)]
pub(crate) mod tests {
use crate::TealUserdata;
pub(crate) struct Vec3 {
x: f32,
y: f32,
z: f32,
}
impl TealUserdata for Vec3 {
fn name() -> String {
"Vec3".to_string()
}
fn build<'a, 'b>(builder: &'b mut crate::TealUserdataBuilder<'a, Self>) {
builder
.field_getter("x", |_, this| Ok(this.x))
.field_setter("x", |_, this, v| {
this.x = v;
Ok(())
})
.field_getter("y", |_, this| Ok(this.y))
.field_setter("y", |_, this, v| {
this.y = v;
Ok(())
})
.field_getter("z", |_, this| Ok(this.z))
.field_setter("z", |_, this, v| {
this.z = v;
Ok(())
})
.set_arg_names(&["x", "y", "z"])
.function("new", |_, (x, y, z)| {
Ok(Vec3 {
x,
y,
z
})
})
.set_arg_names(&["scalar"])
.method("do_something", |_, this, scalar: f32| {
Ok(this.x * scalar)
});
}
}
#[test]
fn simple_teal_ud() {
}
}

93
src/teal/walker.rs Normal file
View File

@ -0,0 +1,93 @@
use crate::{function_desc, AsLua, FieldDesc, FromLuaVec, FunctionDesc, GenerateContext, GenerateTealDefinition, LuaTypeName, Record, TealUserdata, TealUserdataBuilder, Type};
#[derive(Default)]
pub struct TypeWalker {
records: Vec<Record>,
global_fields: Vec<FieldDesc>,
global_funcs: Vec<FunctionDesc>,
}
impl TypeWalker {
pub fn new() -> Self {
Self::default()
}
pub fn walk_type<T: TealUserdata>(&mut self) {
let mut ud = TealUserdataBuilder::<T>::new();
T::build(&mut ud);
let fields = ud.fields.values().cloned().collect();
let functions = ud.funcs.values().cloned().collect();
let record = Record {
ty: Type { name: T::get_lua_name() },
fields,
functions,
};
self.records.push(record);
}
pub fn add_global_field<T: LuaTypeName>(&mut self, name: &str) {
let field = FieldDesc {
name: name.to_string(),
ty: Type::new::<T>(),
can_set: false,
};
self.global_fields.push(field);
}
pub fn add_global_func<'a, Args, Ret>(&mut self, name: &str, arg_names: &[&str])
where
Args: FromLuaVec<'a> + LuaTypeName,
Ret: AsLua<'a> + LuaTypeName
{
let arg_names: Vec<_> = arg_names.iter().map(|s| s.to_string()).collect();
let mut desc = function_desc::<Args, Ret>(&mut Some(arg_names), name);
desc.is_field = true;
self.global_funcs.push(desc);
}
/// Creates the content of a `.d.tl` file.
pub fn to_type_def(&self) -> String {
let mut gen_ctx = GenerateContext::default();
for field in self.global_fields.iter() {
field.generate(&mut gen_ctx);
}
if !self.global_fields.is_empty() {
gen_ctx.write_new_line();
}
for funcs in self.global_funcs.iter() {
funcs.generate(&mut gen_ctx);
}
if !self.global_funcs.is_empty() {
gen_ctx.write_new_line();
}
for record in self.records.iter() {
record.generate(&mut gen_ctx);
}
gen_ctx.content
}
}
#[cfg(test)]
mod tests {
use crate::TypeWalker;
use crate::teal::userdata::tests::Vec3;
#[test]
fn vec3_type_walk() {
let mut walker = TypeWalker::new();
walker.walk_type::<Vec3>();
walker.add_global_field::<f64>("multiplier");
walker.add_global_func::<(f32, f64, usize), i32>("do_cool_math", &["a", "b", "z"]);
println!("{}", walker.to_type_def());
}
}

View File

@ -8,7 +8,7 @@ pub struct Vec2 {
} }
impl Userdata for Vec2 { impl Userdata for Vec2 {
fn build<'a>(_state: &State, builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> { fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) {
builder builder
.field_getter("x", |_, this| Ok(this.x)) .field_getter("x", |_, this| Ok(this.x))
.field_getter("y", |_, this| Ok(this.y)) .field_getter("y", |_, this| Ok(this.y))
@ -58,8 +58,6 @@ impl Userdata for Vec2 {
y: ly + ry, y: ly + ry,
}) })
}); });
Ok(())
} }
fn name() -> String { fn name() -> String {

View File

@ -47,9 +47,9 @@ impl<'a, T: Userdata> From<Ref<'a, T>> for UserdataRef<'static, T> {
} }
impl<'a, T: Userdata + 'static> Userdata for UserdataRef<'a, T> { impl<'a, T: Userdata + 'static> Userdata for UserdataRef<'a, T> {
fn build<'b>(state: &State, builder: &mut UserdataBuilder<'b, Self>) -> crate::Result<()> { fn build<'b>(builder: &mut UserdataBuilder<'b, Self>) {
let mut other = UserdataBuilder::<T>::new(); let mut other = UserdataBuilder::<T>::new();
T::build(state, &mut other)?; T::build(&mut other);
builder.expand_with(other); builder.expand_with(other);
@ -75,8 +75,6 @@ impl<'a, T: Userdata + 'static> Userdata for UserdataRef<'a, T> {
if builder.wrapped_getter_mut.set(Box::new(mut_getter)).is_err() { if builder.wrapped_getter_mut.set(Box::new(mut_getter)).is_err() {
panic!("Somehow the wrapped mutable getter has already been set"); panic!("Somehow the wrapped mutable getter has already been set");
} }
Ok(())
} }
fn name() -> String { fn name() -> String {

View File

@ -56,9 +56,9 @@ impl<'a, T: Userdata> From<RefMut<'a, T>> for UserdataRefMut<'static, T> {
} }
impl<'a, T: Userdata + 'static> Userdata for UserdataRefMut<'a, T> { impl<'a, T: Userdata + 'static> Userdata for UserdataRefMut<'a, T> {
fn build<'b>(state: &State, builder: &mut UserdataBuilder<'b, Self>) -> crate::Result<()> { fn build<'b>(builder: &mut UserdataBuilder<'b, Self>) {
let mut other = UserdataBuilder::<T>::new(); let mut other = UserdataBuilder::<T>::new();
T::build(state, &mut other)?; T::build(&mut other);
builder.expand_with(other); builder.expand_with(other);
@ -91,8 +91,6 @@ impl<'a, T: Userdata + 'static> Userdata for UserdataRefMut<'a, T> {
if builder.wrapped_getter_mut.set(Box::new(getter_mut)).is_err() { if builder.wrapped_getter_mut.set(Box::new(getter_mut)).is_err() {
panic!("Somehow the wrapped mutable getter has already been set"); panic!("Somehow the wrapped mutable getter has already been set");
} }
Ok(())
} }
fn name() -> String { fn name() -> String {

View File

@ -107,7 +107,7 @@ impl<'a> PushToLuaStack<'a> for MetaMethod {
pub trait Userdata: Sized { pub trait Userdata: Sized {
fn name() -> String; fn name() -> String;
fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()>; fn build<'a>(builder: &mut UserdataBuilder<'a, Self>);
} }
impl<'a, T: Userdata + 'static> AsLua<'a> for T { impl<'a, T: Userdata + 'static> AsLua<'a> for T {

View File

@ -11,14 +11,12 @@ impl<T: Userdata> UserdataProxy<T> {
} }
impl<T: Userdata> Userdata for UserdataProxy<T> { impl<T: Userdata> Userdata for UserdataProxy<T> {
fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { fn build<'a>(builder: &mut UserdataBuilder<'a, Self>){
let mut other = UserdataBuilder::<T>::new(); let mut other = UserdataBuilder::<T>::new();
T::build(state, &mut other)?; T::build(&mut other);
// only the functions need to be added since they're the only thing usable from a proxy // only the functions need to be added since they're the only thing usable from a proxy
builder.functions = other.functions; builder.functions = other.functions;
Ok(())
} }
fn name() -> String { fn name() -> String {