use mlua::{MetaMethod, UserData};
use mlua_extras::{
Typed, mlua,
typed::{
Type, TypedDataFields, TypedDataMethods, TypedUserData, WrappedBuilder,
generator::{Definition, DefinitionFileGenerator, Definitions},
},
};
use std::path::PathBuf;
#[derive(Typed)]
enum Kind {
A(String),
B {
name: String,
data: String,
},
#[allow(unused)]
C,
}
impl Kind {
fn get_data(&self) -> String {
match self {
Self::A(data) => data.clone(),
Self::B { data, .. } => data.clone(),
Self::C => "".to_string(),
}
}
}
impl UserData for Kind {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
let mut wrapper = WrappedBuilder::new(fields);
<Self as TypedUserData>::add_fields(&mut wrapper);
}
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
let mut wrapper = mlua_extras::typed::WrappedBuilder::new(methods);
<Self as TypedUserData>::add_methods(&mut wrapper);
}
}
impl TypedUserData for Kind {
fn add_fields<T: TypedDataFields<Self>>(fields: &mut T) {
fields.add_field_method_get("name", |_lua, this: &Self| match this {
Self::B { name, .. } => Ok(name.clone()),
_ => Err(mlua::Error::runtime(
"Kind does not contain field 'name' in it's current variant",
)),
});
fields.add_field_method_get("data", |_lua, this: &Self| {
match this {
Self::B { data, .. } => Ok(data.clone()),
_ => Err(mlua::Error::runtime(
"Kind does not contain field 'data' in it's current variant",
)),
}
});
fields
.coerce(Type::named("KindVariant"))
.add_field_function_get("_variant", |_lua, this| {
match *this.borrow::<Self>().unwrap() {
Self::A(_) => Ok("A"),
Self::B { .. } => Ok("B"),
Self::C => Ok("C"),
}
});
}
fn add_methods<T: TypedDataMethods<Self>>(methods: &mut T) {
methods
.index::<String>(1, "Kind::A variant data")
.add_meta_method(MetaMethod::Index, |_lua, this: &Self, key: usize| {
match key {
1 => match this {
Self::A(value) => Ok(value.clone()),
_ => Err(mlua::Error::runtime(format!(
"Kind does not contain index '{key}' in it's current variant"
))),
},
_ => Err(mlua::Error::runtime(format!(
"Kind does not contain index '{key}' in it's current variant"
))),
}
});
methods.add_method("getData", |_lua, this: &Self, _: ()| Ok(this.get_data()))
}
}
fn main() -> mlua::Result<()> {
let lua = mlua::Lua::new();
lua.globals().set("KindA", Kind::A("Test".into()))?;
lua.globals().set(
"KindB",
Kind::B {
name: "Test".into(),
data: "Test Data".into(),
},
)?;
if let Err(err) = lua
.load(
r#"
print(KindA[1])
-- print(KindA["1"])
print(KindB.name)
print(KindB.data)
local ok, value = pcall(function () return KindA.name end)
print('KindA.name', ok, tostring(value):match("(.-)\n") or tostring(value))
ok, value = pcall(function () return KindA.data end)
print('KindA.data', ok, tostring(value):match("(.-)\n") or tostring(value))
ok, value = pcall(function() return KindB[1] end)
print('KindB[1]', ok, tostring(value):match("(.-)\n") or tostring(value))
print('KindA:getData()', KindA:getData())
print('KindB:getData()', KindB:getData())
"#,
)
.eval::<()>()
{
eprintln!("{err}");
}
let definitions: Definitions = Definitions::start()
.define(
"enum_and_tuple",
Definition::start()
.register::<Kind>("Kind")
.register_as("KindVariant", Type::literal("A") | "B" | "C"),
)
.finish();
let types_path = PathBuf::from("examples/types");
if !types_path.exists() {
std::fs::create_dir_all(&types_path).unwrap();
}
let dfg = DefinitionFileGenerator::new(definitions.clone());
for (name, writer) in dfg.iter() {
println!("==== Generated \x1b[1;33mexample/types/{name}\x1b[0m ====");
writer.write_file(types_path.join(name)).unwrap();
}
Ok(())
}