Compare commits
No commits in common. "feb93f2b4ed7063e4fba366377b039ba4f9026ef" and "936ea606343dbcf5321b4b988de65daf8769ff3f" have entirely different histories.
feb93f2b4e
...
936ea60634
|
@ -8,7 +8,3 @@ 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"]
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
[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"
|
|
|
@ -1,114 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
use elua_derive::embed_compiler;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn from_github() {
|
|
||||||
let compiler = embed_compiler!(Github);
|
|
||||||
}
|
|
|
@ -43,11 +43,6 @@ 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;
|
||||||
|
|
||||||
|
|
|
@ -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(&mut builder);
|
T::build(self, &mut builder)?;
|
||||||
|
|
||||||
let getters = builder.field_getters;
|
let getters = builder.field_getters;
|
||||||
let setters = builder.field_setters;
|
let setters = builder.field_setters;
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
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>,
|
|
||||||
pub docs: Option<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
pub docs: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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
197
src/teal/mod.rs
|
@ -1,197 +0,0 @@
|
||||||
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 }
|
|
|
@ -1,60 +0,0 @@
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,394 +0,0 @@
|
||||||
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>,
|
|
||||||
pub(crate) type_docs: Vec<String>, // each element is a new line
|
|
||||||
func_arg_names: Option<Vec<String>>,
|
|
||||||
/// docs that will be added to a field or function that will soon be added
|
|
||||||
pub(crate) queued_docs: 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(),
|
|
||||||
type_docs: Default::default(),
|
|
||||||
func_arg_names: None,
|
|
||||||
queued_docs: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn function_desc<'a, A, R>(func_arg_names: &mut Option<Vec<String>>, docs: &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 docs = docs.take();
|
|
||||||
|
|
||||||
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,
|
|
||||||
docs,
|
|
||||||
};
|
|
||||||
|
|
||||||
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(),
|
|
||||||
type_docs: Default::default(),
|
|
||||||
func_arg_names: None,
|
|
||||||
queued_docs: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add documentation for this type
|
|
||||||
pub fn document_type(&mut self, text: &str) -> &mut Self {
|
|
||||||
self.type_docs.push(text.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add documentation for a field, or function that will added right after this
|
|
||||||
pub fn document(&mut self, text: &str) -> &mut Self {
|
|
||||||
if self.queued_docs.is_none() {
|
|
||||||
self.queued_docs = Some(Vec::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
let docs = self.queued_docs.as_mut().unwrap();
|
|
||||||
docs.push(text.to_string());
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
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 docs = self.queued_docs.take().unwrap_or_default();
|
|
||||||
|
|
||||||
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,
|
|
||||||
docs,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 docs = self.queued_docs.take().unwrap_or_default();
|
|
||||||
|
|
||||||
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,
|
|
||||||
docs,
|
|
||||||
};
|
|
||||||
|
|
||||||
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, &mut self.queued_docs, 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, &mut self.queued_docs, 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, &mut self.queued_docs, 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, &mut self.queued_docs, 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() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
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>,
|
|
||||||
func_arg_names: Option<Vec<String>>,
|
|
||||||
/// docs that will be added to a field or function that will soon be added
|
|
||||||
queued_docs: Option<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add documentation for a field, or function that will added right after this
|
|
||||||
pub fn document(&mut self, text: &str) -> &mut Self {
|
|
||||||
if self.queued_docs.is_none() {
|
|
||||||
self.queued_docs = Some(Vec::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
let docs = self.queued_docs.as_mut().unwrap();
|
|
||||||
docs.push(text.to_string());
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_global_field<T: LuaTypeName>(&mut self, name: &str) {
|
|
||||||
let docs = self.queued_docs.take().unwrap_or_default();
|
|
||||||
|
|
||||||
let field = FieldDesc {
|
|
||||||
name: name.to_string(),
|
|
||||||
ty: Type::new::<T>(),
|
|
||||||
can_set: false,
|
|
||||||
docs,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.global_fields.push(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_global_func<'a, Args, Ret>(&mut self, name: &str)
|
|
||||||
where
|
|
||||||
Args: FromLuaVec<'a> + LuaTypeName,
|
|
||||||
Ret: AsLua<'a> + LuaTypeName
|
|
||||||
{
|
|
||||||
let mut desc = function_desc::<Args, Ret>(&mut self.func_arg_names, &mut self.queued_docs, 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.document("This function does some very cool math that does a lot.");
|
|
||||||
walker.set_arg_names(&["a", "b", "z"]);
|
|
||||||
walker.add_global_func::<(f32, f64, usize), i32>("do_cool_math");
|
|
||||||
|
|
||||||
println!("{}", walker.to_type_def());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ pub struct Vec2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Userdata for Vec2 {
|
impl Userdata for Vec2 {
|
||||||
fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) {
|
fn build<'a>(_state: &State, builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> {
|
||||||
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,6 +58,8 @@ impl Userdata for Vec2 {
|
||||||
y: ly + ry,
|
y: ly + ry,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name() -> String {
|
fn name() -> String {
|
||||||
|
|
|
@ -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>(builder: &mut UserdataBuilder<'b, Self>) {
|
fn build<'b>(state: &State, builder: &mut UserdataBuilder<'b, Self>) -> crate::Result<()> {
|
||||||
let mut other = UserdataBuilder::<T>::new();
|
let mut other = UserdataBuilder::<T>::new();
|
||||||
T::build(&mut other);
|
T::build(state, &mut other)?;
|
||||||
|
|
||||||
builder.expand_with(other);
|
builder.expand_with(other);
|
||||||
|
|
||||||
|
@ -75,6 +75,8 @@ 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 {
|
||||||
|
|
|
@ -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>(builder: &mut UserdataBuilder<'b, Self>) {
|
fn build<'b>(state: &State, builder: &mut UserdataBuilder<'b, Self>) -> crate::Result<()> {
|
||||||
let mut other = UserdataBuilder::<T>::new();
|
let mut other = UserdataBuilder::<T>::new();
|
||||||
T::build(&mut other);
|
T::build(state, &mut other)?;
|
||||||
|
|
||||||
builder.expand_with(other);
|
builder.expand_with(other);
|
||||||
|
|
||||||
|
@ -91,6 +91,8 @@ 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 {
|
||||||
|
|
|
@ -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>(builder: &mut UserdataBuilder<'a, Self>);
|
fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Userdata + 'static> AsLua<'a> for T {
|
impl<'a, T: Userdata + 'static> AsLua<'a> for T {
|
||||||
|
|
|
@ -11,12 +11,14 @@ impl<T: Userdata> UserdataProxy<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Userdata> Userdata for UserdataProxy<T> {
|
impl<T: Userdata> Userdata for UserdataProxy<T> {
|
||||||
fn build<'a>(builder: &mut UserdataBuilder<'a, Self>){
|
fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> {
|
||||||
let mut other = UserdataBuilder::<T>::new();
|
let mut other = UserdataBuilder::<T>::new();
|
||||||
T::build(&mut other);
|
T::build(state, &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 {
|
||||||
|
|
Loading…
Reference in New Issue