| Crates.io | mlua-magic-macros |
| lib.rs | mlua-magic-macros |
| version | 0.2.0 |
| created_at | 2025-11-07 21:04:50.629749+00 |
| updated_at | 2025-12-13 05:01:34.597173+00 |
| description | Simplify mlua type conversions like magic! |
| homepage | |
| repository | https://github.com/Renderthegreat/mlua_magic |
| max_upload_size | |
| id | 1922167 |
| size | 54,274 |
mlua_magic_macrosSimple, magical proc-macros to export Rust structs and enums to mlua with minimal boilerplate.
This crate provides a set of attribute macros that write the "magic" glue code to automatically generate impl mlua::UserData for your Rust types.
player.hp).PlayerStatus.Idle()).&self, &mut self, and static) as Lua methods (player:take_damage(10)).Add this to your Cargo.toml:
[dependencies]
mlua = { version = "0.11.4", features = ["lua54", "macros"] } # Or the version you are using
mlua-magic-macros = "0.1.2" # Or the version you are using
Here is a complete, copy-pasteable example.
Define your types and "decorate" them with the macros.
use ::mlua::prelude::*;
use ::mlua_magic_macros;
// STEP 1: Decorate your enum
#[derive(Debug, Copy, Clone, Default, PartialEq)]
#[mlua_magic_macros::enumeration]
pub enum PlayerStatus {
#[default] Idle,
Walking,
Attacking,
}
// STEP 2: Compile the UserData impl for the enum
// This generates `impl mlua::UserData for PlayerStatus`
mlua_magic_macros::compile!(type_path = PlayerStatus, variants = true);
// STEP 1: Decorate your struct
#[derive(Debug, Clone, Default)]
#[mlua_magic_macros::structure]
pub struct Player {
name: String,
hp: i32,
status: PlayerStatus,
}
// STEP 1 (continued): Decorate the impl block
#[mlua_magic_macros::implementation]
impl Player {
// Registered as a static "constructor": Player.new()
pub fn new(name: String) -> Self {
return Self {
name,
hp: 100,
status: PlayerStatus::Idle,
};
}
// Registered as a `&mut self` method: player:take_damage()
pub fn take_damage(&mut self, amount: i32) -> () {
self.hp -= amount;
if self.hp < 0 {
self.hp = 0;
};
}
// Registered as a `&self` method: player:is_alive()
pub fn is_alive(&self) -> bool {
return self.hp > 0;
}
}
// STEP 2: Compile the UserData impl for the struct
// This generates `impl mlua::UserData for Player`
mlua_magic_macros::compile!(type_path = Player, fields = true, methods = true);
// STEP 3: Load the types into a Lua instance
fn main() -> LuaResult<()> {
let lua = Lua::new();
// This makes `Player` and `PlayerStatus` available as globals in Lua
mlua_magic_macros::load!(lua, Player, PlayerStatus);
// See the Lua script below!
run_lua_code(&lua)?;
return Ok(());
}
Now you can call your Rust code from Lua as if it were a native Lua table.
-- run_lua_code.lua
-- Call the static `new` function
local player = Player.new("LuaHero");
print("Player created:");
-- Access struct fields directly (from `#[structure]`)
print("Player name:", player.name);
print("Player HP:", player.hp);
-- Access enum variants (from `#[enumeration]`)
print("Player status:", player.status); -- "Idle"
print("Is alive?", player:is_alive()); -- `true`
-- Call a `&mut self` method
player:take_damage(30);
print("New player HP:", player.hp); -- 70
-- You can even set fields!
player.status = PlayerStatus.Attacking();
print("Player status:", player.status); -- "Attacking"
player:take_damage(80)
print("Player HP after final hit:", player.hp); -- 0
print("Is alive?", player:is_alive()); -- `false`
This crate uses a 3-step process:
#[...]) to your types to tell the macros what to export.compile!(...) macro to generate the impl mlua::UserData block.load!(...) macro at runtime to register your types with a Lua instance. You can also manually load your types if our API doesn't allow you to do something.| Macro | Target | Purpose |
|---|---|---|
#[enumeration] |
enum |
Exposes unit variants as static functions (e.g., MyEnum.VariantA()). |
#[structure] |
struct |
Exposes fields as readable/writable properties (e.g., my_struct.field). |
#[implementation] |
impl |
Exposes functions as methods (e.g., MyType.new(), my_inst:do_thing()). |
The compile! macro generates the final impl mlua::UserData and impl mlua::FromLua for your type.
mlua_magic_macros::compile!(
type_path = MyType, // The name of the struct/enum
fields = true, // Include fields from `#[structure]`?
methods = true, // Include methods from `#[implementation]`?
variants = true // Include variants from `#[enumeration]`?
);
The load! macro registers your compiled types as globals in Lua.
let lua = Lua::new();
// This is like running:
// _G.Player = (proxy for Player UserData)
// _G.PlayerStatus = (proxy for PlayerStatus UserData)
mlua_magic_macros::load!(lua, Player, PlayerStatus);
This crate is licensed under the MIT license.