use std::path::PathBuf; use mlua_extras::{ mlua::{self, FromLua, Lua, LuaSerdeExt, MetaMethod, UserDataMethods, Value, Variadic}, typed::{ generator::{Definition, Definitions, DefinitionFileGenerator}, TypedDataFields, TypedDataMethods, TypedUserData, }, extras::LuaExtras, Typed, UserData, }; use serde::Deserialize; #[derive(Default, Debug, Clone, Copy, Typed, Deserialize)] enum SystemColor { #[default] Black, Red, Green, Yellow, Blue, Cyan, Magenta, White, } #[derive(Debug, Clone, Copy, Typed, UserData, Deserialize)] #[serde(untagged)] enum Color { System(SystemColor), Xterm(u8), Rgb(u8, u8, u8), } impl Default for Color { fn default() -> Self { Color::System(SystemColor::default()) } } impl Color { pub fn background_ansi(&self) -> String { match self { Self::System(system) => match system { SystemColor::Black => "\x1b[40m".into(), SystemColor::Red => "\x1b[41m".into(), SystemColor::Green => "\x1b[42m".into(), SystemColor::Yellow => "\x1b[43m".into(), SystemColor::Blue => "\x1b[44m".into(), SystemColor::Magenta => "\x1b[45m".into(), SystemColor::Cyan => "\x1b[46m".into(), SystemColor::White => "\x1b[47m".into(), }, Self::Xterm(xterm) => format!("\x1b[48;5;{xterm}m"), Self::Rgb(r, g, b) => format!("\x1b[48;2;{r};{g};{b}m"), } } } impl TypedUserData for Color { fn add_documentation>(docs: &mut F) { docs.add("Representation of a color"); } fn add_methods<'lua, T: TypedDataMethods<'lua, Self>>(methods: &mut T) { methods.add_meta_method(MetaMethod::ToString, |_lua, this, _: ()| { Ok(format!("{this:?}")) }); } } impl<'lua> FromLua<'lua> for Color { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> mlua::prelude::LuaResult { match value { Value::UserData(data) => data.borrow::().map(|v| *v), // Use serde deserialize if not userdata other => lua.from_value(other), } } } #[derive(Debug, Clone, Copy, UserData, Typed, Deserialize)] struct Example { color: Color, } impl Default for Example { fn default() -> Self { Self { color: Color::Rgb(30, 132, 129), } } } impl<'lua> FromLua<'lua> for Example { fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> mlua::prelude::LuaResult { match value { Value::UserData(data) => data.borrow::().map(|v| *v), other => lua.from_value(other), } } } impl TypedUserData for Example { fn add_documentation>(docs: &mut F) { docs.add("This is a doc comment section for the overall type"); } fn add_fields<'lua, F: TypedDataFields<'lua, Self>>(fields: &mut F) { fields .document("Example complex type") .add_field_method_get_set( "color", |_lua, this| Ok(this.color), |_lua, this, clr: Color| { this.color = clr; Ok(()) }, ); } fn add_methods<'lua, T: TypedDataMethods<'lua, Self>>(methods: &mut T) { methods.document("print all items").add_function( "printAll", |_lua, all: Variadic| { println!( "{}", all.iter().map(|v| v.as_str()).collect::>().join(" ") ); Ok(()) }, ); methods.add_meta_method(MetaMethod::ToString, |_lua, this, ()| { Ok(format!("{this:?}")) }); } } fn main() -> mlua::Result<()> { let lua = Lua::new(); // ===== Setup Lua Engine ===== lua.set_global("example", Example::default())?; lua.set_global_function("greet", |_lua, name: String| { println!("Hello, {name}"); Ok(()) })?; lua.set_global_function("printColor", |_lua, color: Color| { println!("{} \x1b[0m {color:?}", color.background_ansi()); Ok(()) })?; // ===== Generate Types and Definition Files ===== let definitions = Definitions::start() .define("init", Definition::start() .register_enum::()? .register_enum::()? .register_class::() .value::("example") .function::("greet", ()) .function::("printColor", ()) ) .finish(); let types_path = PathBuf::from("examples/types"); if !types_path.exists() { std::fs::create_dir_all(&types_path).unwrap(); } let gen = DefinitionFileGenerator::new(definitions); for (name, writer) in gen.iter() { println!("==== Generated \x1b[1;33mexample/types/{name}\x1b[0m ===="); writer.write_file(types_path.join(name)).unwrap(); } println!(); // ===== Run user defined file... This will default if file doesn't exist ===== let default = r#" example.printAll("Some", "text", "printed", "with", "a", "single", "space") printColor(example.color) printColor({ 30, 129, 20 }) printColor(211) printColor("Blue") "#; let user_file = PathBuf::from("examples/typed.lua"); if user_file.exists() { if let Err(err) = lua.load(user_file).eval::() { eprintln!("{err}"); } } else { println!( "\x1b[1;36mNOTE\x1b[22;39m This is the default example lua code for the typed example" ); println!( "\x1b[1;36mNOTE\x1b[22;39m create a file at `examles/typed.lua` to run your own code. \ LuaLS should pull in the generated `examples/types/init.d.lua` automatically" ); println!(); if let Err(err) = lua.load(default).eval::() { eprintln!("{err}"); } } Ok(()) }