luars/lua_value/userdata_trait.rs
1// Trait-based Userdata system for Lua-rs
2//
3// Instead of using Lua's traditional metatable-based approach for userdata access,
4// we leverage Rust's trait system for direct, type-safe dispatch of field access,
5// method calls, and metamethods.
6//
7// Key design principles:
8// 1. Trait-based dispatch (no metatable lookup for known operations)
9// 2. Auto-derive from Rust structs via `#[derive(LuaUserData)]`
10// 3. Automatic metamethod generation from Rust trait impls (Display → __tostring, Ord → __lt, etc.)
11// 4. Backward compatibility via `as_any()` downcasting
12// 5. Metatables still work as fallback for Lua-level customization
13
14use std::any::Any;
15use std::fmt;
16
17use crate::lua_vm::CFunction;
18
19/// Intermediate value type for userdata field/method returns.
20///
21/// Since `LuaValue` requires GC-allocated strings, trait methods return `UdValue`
22/// which the VM converts to proper `LuaValue` (interning strings as needed).
23pub enum UdValue {
24 Nil,
25 Boolean(bool),
26 Integer(i64),
27 Number(f64),
28 /// A Rust string — will be interned by the VM when converting to LuaValue
29 Str(String),
30 /// A light C function — used for returning methods from `get_field`
31 Function(CFunction),
32 /// Borrowed reference to another userdata's inner value (as operand in
33 /// arithmetic/comparison). Valid only during the trait method call.
34 /// Use [`UdValue::as_userdata_ref`] to safely downcast.
35 UserdataRef(*const dyn Any),
36 /// Owned userdata value (as return from arithmetic trait methods).
37 /// The VM allocates this as a new GC-managed userdata.
38 UserdataOwned(Box<dyn UserDataTrait>),
39}
40
41impl Clone for UdValue {
42 fn clone(&self) -> Self {
43 match self {
44 UdValue::Nil => UdValue::Nil,
45 UdValue::Boolean(b) => UdValue::Boolean(*b),
46 UdValue::Integer(i) => UdValue::Integer(*i),
47 UdValue::Number(n) => UdValue::Number(*n),
48 UdValue::Str(s) => UdValue::Str(s.clone()),
49 UdValue::Function(f) => UdValue::Function(*f),
50 UdValue::UserdataRef(p) => UdValue::UserdataRef(*p),
51 UdValue::UserdataOwned(_) => {
52 // Cannot clone owned userdata — return Nil as fallback.
53 // This should not happen in practice; the VM consumes owned
54 // userdata immediately.
55 UdValue::Nil
56 }
57 }
58 }
59}
60
61impl UdValue {
62 #[inline]
63 pub fn is_nil(&self) -> bool {
64 matches!(self, UdValue::Nil)
65 }
66
67 /// Try to downcast a `UserdataRef` operand to a concrete type.
68 ///
69 /// Returns `Some(&T)` if the operand is a userdata of type `T`.
70 /// This is the primary way to access the other operand in arithmetic
71 /// trait methods when both operands are userdata.
72 ///
73 /// # Safety
74 /// The returned reference borrows from the GC-managed userdata on the
75 /// Lua stack. It is valid for the duration of the trait method call.
76 ///
77 /// # Example (generated by derive macro)
78 /// ```ignore
79 /// fn lua_add(&self, other: &UdValue) -> Option<UdValue> {
80 /// if let Some(o) = other.as_userdata_ref::<Vec2>() {
81 /// Some(UdValue::from_userdata(Vec2 { x: self.x + o.x, y: self.y + o.y }))
82 /// } else {
83 /// None
84 /// }
85 /// }
86 /// ```
87 #[inline]
88 pub fn as_userdata_ref<T: 'static>(&self) -> Option<&T> {
89 match self {
90 UdValue::UserdataRef(ptr) => {
91 // SAFETY: The pointer originates from a GC-managed userdata that
92 // is alive on the Lua stack during this call. The VM guarantees
93 // the pointer remains valid for the duration of the trait method.
94 let any_ref: &dyn Any = unsafe { &**ptr };
95 any_ref.downcast_ref::<T>()
96 }
97 _ => None,
98 }
99 }
100
101 /// Wrap a value that implements `UserDataTrait` into an owned `UdValue`.
102 ///
103 /// Use this as the return value from arithmetic trait methods when the
104 /// result is a new userdata (e.g., `Vec2 + Vec2 → Vec2`).
105 #[inline]
106 pub fn from_userdata<T: UserDataTrait>(value: T) -> Self {
107 UdValue::UserdataOwned(Box::new(value))
108 }
109}
110
111/// Describes how a userdata type can be accessed from Lua.
112///
113/// This trait provides rich, typed access to struct fields, methods, and standard
114/// operations. The `#[derive(LuaUserData)]` macro auto-implements this trait by
115/// exposing public fields. Methods are exposed via `#[lua_methods]` attribute macro
116/// on impl blocks, which generates static C wrapper functions returned from `get_field`
117/// as `UdValue::Function(cfunction)`.
118///
119/// # Dispatch priority (when Lua accesses `obj.key`):
120/// 1. `get_field(key)` — field or method access (fields return value, methods return CFunction)
121/// 2. Metatable `__index` — traditional Lua fallback
122///
123/// # Example (manual implementation)
124/// ```ignore
125/// struct Point { x: f64, y: f64 }
126///
127/// impl UserDataTrait for Point {
128/// fn type_name(&self) -> &'static str { "Point" }
129///
130/// fn get_field(&self, key: &str) -> Option<UdValue> {
131/// match key {
132/// "x" => Some(UdValue::Number(self.x)),
133/// "y" => Some(UdValue::Number(self.y)),
134/// _ => None,
135/// }
136/// }
137///
138/// fn set_field(&mut self, key: &str, value: UdValue) -> Option<Result<(), String>> {
139/// match key {
140/// "x" => match value {
141/// UdValue::Number(n) => { self.x = n; Some(Ok(())) }
142/// _ => Some(Err("x must be a number".into()))
143/// }
144/// _ => None,
145/// }
146/// }
147///
148/// fn as_any(&self) -> &dyn Any { self }
149/// fn as_any_mut(&mut self) -> &mut dyn Any { self }
150/// }
151/// ```
152pub trait UserDataTrait: 'static {
153 // ==================== Identity ====================
154
155 /// Returns the type name displayed in error messages and `type()` calls.
156 /// For derive macro: uses the struct name.
157 fn type_name(&self) -> &'static str;
158
159 // ==================== Field Access ====================
160
161 /// Get a field value by name.
162 /// Returns `Some(value)` if the field exists, `None` to fall through to metatable.
163 fn get_field(&self, _key: &str) -> Option<UdValue> {
164 None
165 }
166
167 /// Set a field value by name.
168 /// Returns:
169 /// - `Some(Ok(()))` — field was set successfully
170 /// - `Some(Err(msg))` — field exists but value is invalid (type mismatch, etc.)
171 /// - `None` — field not found, fall through to metatable `__newindex`
172 fn set_field(&mut self, _key: &str, _value: UdValue) -> Option<Result<(), String>> {
173 None
174 }
175
176 // ==================== Metamethods ====================
177 // These are auto-generated by the derive macro when the struct
178 // implements the corresponding Rust trait. Return None = not supported.
179
180 /// `__tostring`: String representation.
181 /// Auto-generated when struct implements `Display`.
182 fn lua_tostring(&self) -> Option<String> {
183 None
184 }
185
186 /// `__eq`: Equality comparison.
187 /// Auto-generated when struct implements `PartialEq`.
188 /// `other` is guaranteed to be a `&dyn UserDataTrait` — downcast via `as_any()`.
189 fn lua_eq(&self, _other: &dyn UserDataTrait) -> Option<bool> {
190 None
191 }
192
193 /// `__lt`: Less-than comparison.
194 /// Auto-generated when struct implements `PartialOrd`.
195 fn lua_lt(&self, _other: &dyn UserDataTrait) -> Option<bool> {
196 None
197 }
198
199 /// `__le`: Less-or-equal comparison.
200 /// Auto-generated when struct implements `PartialOrd`.
201 fn lua_le(&self, _other: &dyn UserDataTrait) -> Option<bool> {
202 None
203 }
204
205 /// `__len`: Length operator (`#obj`).
206 /// Auto-generated when struct has a `.len()` method.
207 fn lua_len(&self) -> Option<UdValue> {
208 None
209 }
210
211 /// `__unm`: Unary minus (`-obj`).
212 /// Auto-generated when struct implements `Neg`.
213 fn lua_unm(&self) -> Option<UdValue> {
214 None
215 }
216
217 /// `__add`: Addition (`obj + other`).
218 /// Auto-generated when struct implements `Add`.
219 fn lua_add(&self, _other: &UdValue) -> Option<UdValue> {
220 None
221 }
222
223 /// `__sub`: Subtraction (`obj - other`).
224 /// Auto-generated when struct implements `Sub`.
225 fn lua_sub(&self, _other: &UdValue) -> Option<UdValue> {
226 None
227 }
228
229 /// `__mul`: Multiplication (`obj * other`).
230 /// Auto-generated when struct implements `Mul`.
231 fn lua_mul(&self, _other: &UdValue) -> Option<UdValue> {
232 None
233 }
234
235 /// `__div`: Division (`obj / other`).
236 /// Auto-generated when struct implements `Div`.
237 fn lua_div(&self, _other: &UdValue) -> Option<UdValue> {
238 None
239 }
240
241 /// `__mod`: Modulo (`obj % other`).
242 /// Auto-generated when struct implements `Rem`.
243 fn lua_mod(&self, _other: &UdValue) -> Option<UdValue> {
244 None
245 }
246
247 /// `__concat`: Concatenation (`obj .. other`).
248 fn lua_concat(&self, _other: &UdValue) -> Option<UdValue> {
249 None
250 }
251
252 /// `__gc`: Called when the userdata is garbage collected.
253 /// Use this for cleanup (closing files, releasing resources, etc.).
254 fn lua_gc(&mut self) {}
255
256 /// `__close`: Called when a to-be-closed variable goes out of scope.
257 fn lua_close(&mut self) {}
258
259 /// `__call`: Makes the userdata callable like a function.
260 ///
261 /// Return `Some(cfunction)` to make `obj(args...)` work from Lua.
262 /// The CFunction receives `self` (the userdata) as arg 1, followed by
263 /// the caller's arguments.
264 ///
265 /// This is checked before the metatable `__call` fallback.
266 ///
267 /// # Example
268 /// ```ignore
269 /// fn lua_call(&self) -> Option<CFunction> {
270 /// fn call_impl(l: &mut LuaState) -> LuaResult<usize> {
271 /// let ud = l.get_arg(1).unwrap();
272 /// let x = l.get_arg(2).and_then(|v| v.as_integer()).unwrap_or(0);
273 /// l.push_value(LuaValue::integer(x * 2))?;
274 /// Ok(1)
275 /// }
276 /// Some(call_impl)
277 /// }
278 /// ```
279 fn lua_call(&self) -> Option<crate::lua_vm::CFunction> {
280 None
281 }
282
283 // ==================== Iteration ====================
284
285 /// Stateless iterator: given the current control variable, return the next
286 /// `(control, value)` pair, or `None` to stop.
287 ///
288 /// This follows Lua's generic-for protocol:
289 /// ```lua
290 /// for k, v in pairs(ud) do ... end
291 /// ```
292 ///
293 /// The control variable starts as `UdValue::Nil`. Implementors decide
294 /// what it represents (e.g., an integer index for sequences).
295 ///
296 /// # Example (Vec-like)
297 /// ```ignore
298 /// fn lua_next(&self, control: &UdValue) -> Option<(UdValue, UdValue)> {
299 /// let idx = match control {
300 /// UdValue::Nil => 0,
301 /// UdValue::Integer(i) => *i as usize,
302 /// _ => return None,
303 /// };
304 /// self.items.get(idx).map(|v| (
305 /// UdValue::Integer((idx + 1) as i64),
306 /// UdValue::Integer(*v as i64),
307 /// ))
308 /// }
309 /// ```
310 fn lua_next(&self, _control: &UdValue) -> Option<(UdValue, UdValue)> {
311 None
312 }
313
314 // ==================== Reflection ====================
315
316 /// List available field names (for debugging, iteration, auto-completion).
317 fn field_names(&self) -> &'static [&'static str] {
318 &[]
319 }
320
321 // ==================== Downcasting ====================
322 // Required for backward compatibility and type-specific access.
323 // The derive macro auto-generates these.
324
325 /// Downcast to `&dyn Any` for type-specific access.
326 fn as_any(&self) -> &dyn Any;
327
328 /// Downcast to `&mut dyn Any` for mutable type-specific access.
329 fn as_any_mut(&mut self) -> &mut dyn Any;
330}
331
332// ==================== UdValue ↔ Rust type conversions ====================
333
334impl From<bool> for UdValue {
335 fn from(b: bool) -> Self {
336 UdValue::Boolean(b)
337 }
338}
339
340impl From<i64> for UdValue {
341 fn from(i: i64) -> Self {
342 UdValue::Integer(i)
343 }
344}
345
346impl From<i32> for UdValue {
347 fn from(i: i32) -> Self {
348 UdValue::Integer(i as i64)
349 }
350}
351
352impl From<f64> for UdValue {
353 fn from(n: f64) -> Self {
354 UdValue::Number(n)
355 }
356}
357
358impl From<f32> for UdValue {
359 fn from(n: f32) -> Self {
360 UdValue::Number(n as f64)
361 }
362}
363
364impl From<String> for UdValue {
365 fn from(s: String) -> Self {
366 UdValue::Str(s)
367 }
368}
369
370impl From<&str> for UdValue {
371 fn from(s: &str) -> Self {
372 UdValue::Str(s.to_owned())
373 }
374}
375
376impl<T: Into<UdValue>> From<Option<T>> for UdValue {
377 fn from(opt: Option<T>) -> Self {
378 match opt {
379 Some(v) => v.into(),
380 None => UdValue::Nil,
381 }
382 }
383}
384
385// ==================== UdValue → Rust type extraction ====================
386
387impl UdValue {
388 /// Extract as bool. Follows Lua truthiness: nil and false are false, everything else is true.
389 pub fn to_bool(&self) -> bool {
390 match self {
391 UdValue::Nil => false,
392 UdValue::Boolean(b) => *b,
393 _ => true,
394 }
395 }
396
397 /// Extract as i64 (with optional float→int coercion).
398 pub fn to_integer(&self) -> Option<i64> {
399 match self {
400 UdValue::Integer(i) => Some(*i),
401 UdValue::Number(n) => {
402 let i = *n as i64;
403 if (i as f64) == *n { Some(i) } else { None }
404 }
405 _ => None,
406 }
407 }
408
409 /// Extract as f64 (with optional int→float coercion).
410 pub fn to_number(&self) -> Option<f64> {
411 match self {
412 UdValue::Number(n) => Some(*n),
413 UdValue::Integer(i) => Some(*i as f64),
414 _ => None,
415 }
416 }
417
418 /// Extract as string reference.
419 pub fn to_str(&self) -> Option<&str> {
420 match self {
421 UdValue::Str(s) => Some(s.as_str()),
422 _ => None,
423 }
424 }
425}
426
427impl fmt::Debug for UdValue {
428 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429 match self {
430 UdValue::Nil => write!(f, "Nil"),
431 UdValue::Boolean(b) => write!(f, "Boolean({})", b),
432 UdValue::Integer(i) => write!(f, "Integer({})", i),
433 UdValue::Number(n) => write!(f, "Number({})", n),
434 UdValue::Str(s) => write!(f, "Str({:?})", s),
435 UdValue::Function(_) => write!(f, "Function(<cfunction>)"),
436 UdValue::UserdataRef(_) => write!(f, "UserdataRef(<ptr>)"),
437 UdValue::UserdataOwned(ud) => write!(f, "UserdataOwned({})", ud.type_name()),
438 }
439 }
440}
441
442impl fmt::Display for UdValue {
443 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444 match self {
445 UdValue::Nil => write!(f, "nil"),
446 UdValue::Boolean(b) => write!(f, "{}", b),
447 UdValue::Integer(i) => write!(f, "{}", i),
448 UdValue::Number(n) => write!(f, "{}", n),
449 UdValue::Str(s) => write!(f, "{}", s),
450 UdValue::Function(_) => write!(f, "function"),
451 UdValue::UserdataRef(_) => write!(f, "userdata"),
452 UdValue::UserdataOwned(ud) => write!(f, "{}", ud.type_name()),
453 }
454 }
455}
456
457// ==================== UdValue ↔ LuaValue conversion ====================
458
459/// Convert a `UdValue` to a `LuaValue`.
460///
461/// Most variants are zero-cost. `UdValue::Str` requires GC allocation via `LuaState`.
462/// This is the bridge between trait-based dispatch (which returns `UdValue`) and the
463/// VM's internal representation (`LuaValue`).
464pub fn udvalue_to_lua_value(
465 lua_state: &mut crate::lua_vm::LuaState,
466 udv: UdValue,
467) -> crate::lua_vm::LuaResult<crate::lua_value::LuaValue> {
468 use crate::lua_value::LuaValue;
469 match udv {
470 UdValue::Nil => Ok(LuaValue::nil()),
471 UdValue::Boolean(b) => Ok(LuaValue::boolean(b)),
472 UdValue::Integer(i) => Ok(LuaValue::integer(i)),
473 UdValue::Number(n) => Ok(LuaValue::float(n)),
474 UdValue::Str(s) => lua_state.create_string(&s),
475 UdValue::Function(f) => Ok(LuaValue::cfunction(f)),
476 UdValue::UserdataRef(_) => Ok(LuaValue::nil()), // should not be returned
477 UdValue::UserdataOwned(ud) => {
478 use crate::lua_value::LuaUserdata;
479 let userdata = LuaUserdata::from_boxed(ud);
480 lua_state.create_userdata(userdata)
481 }
482 }
483}
484
485/// Convert a `LuaValue` to a `UdValue`.
486///
487/// Lossless for nil, bool, int, float, string. Other types (table, function, etc.)
488/// become `UdValue::Nil` since they can't be represented in the trait world.
489pub fn lua_value_to_udvalue(value: &crate::lua_value::LuaValue) -> UdValue {
490 if value.is_nil() {
491 UdValue::Nil
492 } else if let Some(b) = value.as_boolean() {
493 UdValue::Boolean(b)
494 } else if let Some(i) = value.as_integer() {
495 UdValue::Integer(i)
496 } else if let Some(n) = value.as_float() {
497 UdValue::Number(n)
498 } else if let Some(s) = value.as_str() {
499 UdValue::Str(s.to_owned())
500 } else if let Some(ud) = value.as_userdata_mut() {
501 // Carry userdata reference so arithmetic trait methods can downcast
502 UdValue::UserdataRef(ud.get_trait().as_any() as *const dyn Any)
503 } else {
504 UdValue::Nil
505 }
506}
507
508// ==================== Convenience macro for simple types ====================
509
510/// Implement `UserDataTrait` for types that only need type name and downcast support.
511/// These types use metatables for their Lua-visible API (e.g., IO file handles).
512///
513/// ```ignore
514/// impl_simple_userdata!(LuaFile, "FILE*");
515/// ```
516#[macro_export]
517macro_rules! impl_simple_userdata {
518 ($ty:ty, $name:expr) => {
519 impl $crate::lua_value::userdata_trait::UserDataTrait for $ty {
520 fn type_name(&self) -> &'static str {
521 $name
522 }
523
524 fn as_any(&self) -> &dyn std::any::Any {
525 self
526 }
527
528 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
529 self
530 }
531 }
532 };
533}
534
535// ==================== Method Provider ====================
536
537/// Blanket trait providing a default no-op method lookup.
538///
539/// When `#[lua_methods]` attribute macro is used on an impl block,
540/// it generates an inherent `__lua_lookup_method` function on the type
541/// that shadows this trait's default. The derive macro's `get_field`
542/// calls `Self::__lua_lookup_method(key)` which resolves to the
543/// inherent method (if defined) or falls back to this default.
544pub trait LuaMethodProvider {
545 fn __lua_lookup_method(_key: &str) -> Option<CFunction> {
546 None
547 }
548}
549
550impl<T> LuaMethodProvider for T {}
551
552// ==================== Type Registration ====================
553
554/// Blanket trait providing a default empty static methods list.
555///
556/// When `#[lua_methods]` is used on an impl block that contains associated
557/// functions (no `self`), it generates an inherent `__lua_static_methods()`
558/// function on the type that shadows this trait's default.
559///
560/// Used by `LuaState::register_type::<T>(name)` to populate the class table.
561pub trait LuaStaticMethodProvider {
562 fn __lua_static_methods() -> &'static [(&'static str, CFunction)] {
563 &[]
564 }
565}
566
567impl<T> LuaStaticMethodProvider for T {}
568
569/// Trait for types that can be registered with Lua via `register_type_of::<T>`.
570///
571/// This trait is explicitly implemented by `#[lua_methods]` (no blanket impl),
572/// so `T::lua_static_methods()` dispatches to the actual generated methods.
573///
574/// Unlike `LuaStaticMethodProvider` (which has a blanket impl that always
575/// returns `&[]`), this trait guarantees that the type has real method info.
576pub trait LuaRegistrable {
577 /// Return all static (associated) methods for type registration.
578 fn lua_static_methods() -> &'static [(&'static str, CFunction)];
579}
580
581// ==================== Enum Export ====================
582
583/// Trait for Rust enums that can be exported to Lua as a table of constants.
584///
585/// Automatically implemented by `#[derive(LuaUserData)]` on C-like enums
586/// (enums with no data fields). Each variant becomes a key-value pair in
587/// a Lua table.
588///
589/// # Example
590///
591/// ```ignore
592/// #[derive(LuaUserData)]
593/// enum Color {
594/// Red, // 0
595/// Green, // 1
596/// Blue, // 2
597/// }
598///
599/// // With explicit discriminants:
600/// #[derive(LuaUserData)]
601/// enum HttpStatus {
602/// Ok = 200,
603/// NotFound = 404,
604/// ServerError = 500,
605/// }
606///
607/// // Register in Lua:
608/// vm.register_enum::<Color>("Color")?;
609/// // Lua: Color.Red == 0, Color.Green == 1, Color.Blue == 2
610/// ```
611pub trait LuaEnum {
612 /// Return variant name-value pairs for Lua table construction.
613 fn variants() -> &'static [(&'static str, i64)];
614
615 /// Return the enum's type name.
616 fn enum_name() -> &'static str;
617}
618
619// ==================== OpaqueUserData ====================
620
621/// Wraps any `T: 'static` as an opaque Lua userdata.
622///
623/// No fields, methods, or metamethods are exposed — the value is a "black box"
624/// in Lua. From Rust you can recover the original type via `downcast_ref::<T>()`.
625///
626/// Use [`LuaVM::push_any`] to create one conveniently.
627///
628/// # Example
629///
630/// ```ignore
631/// // Third-party type you don't control
632/// let client = reqwest::Client::new();
633/// let ud = vm.push_any(client)?;
634/// vm.set_global("http_client", ud)?;
635///
636/// // Later, in a Rust callback:
637/// let client = ud_value.downcast_ref::<reqwest::Client>().unwrap();
638/// ```
639pub struct OpaqueUserData<T: 'static> {
640 value: T,
641}
642
643impl<T: 'static> OpaqueUserData<T> {
644 /// Wrap a value.
645 pub fn new(value: T) -> Self {
646 OpaqueUserData { value }
647 }
648
649 /// Get a reference to the inner value.
650 pub fn inner(&self) -> &T {
651 &self.value
652 }
653
654 /// Get a mutable reference to the inner value.
655 pub fn inner_mut(&mut self) -> &mut T {
656 &mut self.value
657 }
658}
659
660impl<T: 'static> UserDataTrait for OpaqueUserData<T> {
661 fn type_name(&self) -> &'static str {
662 std::any::type_name::<T>()
663 }
664
665 fn as_any(&self) -> &dyn Any {
666 // Downcast to T (not OpaqueUserData<T>) for ergonomic access
667 &self.value
668 }
669
670 fn as_any_mut(&mut self) -> &mut dyn Any {
671 &mut self.value
672 }
673}
674
675// ==================== RefUserData — zero-cost borrowed reference ====================
676
677/// A userdata wrapper that holds a raw pointer to an external `T: UserDataTrait`.
678///
679/// This enables passing Rust objects to Lua **by reference** without transferring
680/// ownership. The object lives on the Rust side; Lua sees a full userdata with
681/// field access, method calls, and metamethods — all forwarded through the pointer.
682///
683/// # Performance
684/// Zero overhead compared to owned userdata — no `Rc`, no `RefCell`, no
685/// atomic operations. The pointer is dereferenced directly on every access.
686///
687/// # Safety
688/// The caller **must** guarantee that the pointee outlives every Lua access.
689/// Accessing the userdata after the Rust object is dropped is undefined behavior.
690/// This is deliberately an `unsafe` API — the same contract as C Lua's light
691/// userdata, but with full trait-based dispatch.
692///
693/// # Example
694/// ```ignore
695/// let mut player = Player::new("Alice", 100);
696///
697/// // Lend to Lua (no ownership transfer)
698/// let ud_val = state.create_userdata_ref(&mut player)?;
699/// state.set_global("player", ud_val)?;
700///
701/// // Lua can read/write fields, call methods — all forwarded to `player`
702/// state.execute_string(r#"
703/// print(player.name) -- "Alice"
704/// player:take_damage(10)
705/// print(player.hp) -- 90
706/// "#)?;
707///
708/// // Back in Rust, `player` reflects the mutations
709/// assert_eq!(player.hp, 90);
710/// ```
711pub struct RefUserData<T: UserDataTrait> {
712 ptr: *mut T,
713}
714
715impl<T: UserDataTrait> RefUserData<T> {
716 /// Create a `RefUserData` from a mutable reference.
717 ///
718 /// # Safety
719 /// The referenced object must outlive all Lua accesses to this userdata.
720 #[inline]
721 pub unsafe fn new(reference: &mut T) -> Self {
722 RefUserData {
723 ptr: reference as *mut T,
724 }
725 }
726
727 /// Create from a raw pointer.
728 ///
729 /// # Safety
730 /// The pointer must be valid and properly aligned for the entire duration
731 /// that Lua can access this userdata.
732 #[inline]
733 pub unsafe fn from_raw(ptr: *mut T) -> Self {
734 RefUserData { ptr }
735 }
736
737 #[inline]
738 fn inner(&self) -> &T {
739 unsafe { &*self.ptr }
740 }
741
742 #[inline]
743 fn inner_mut(&mut self) -> &mut T {
744 unsafe { &mut *self.ptr }
745 }
746}
747
748/// Forward every `UserDataTrait` method to the pointee.
749///
750/// This is intentionally not `'static` on `T` alone — the `'static` bound
751/// comes from `UserDataTrait: 'static`. The raw pointer erases the lifetime;
752/// correctness relies on the caller's safety guarantee.
753impl<T: UserDataTrait> UserDataTrait for RefUserData<T> {
754 #[inline]
755 fn type_name(&self) -> &'static str {
756 self.inner().type_name()
757 }
758
759 #[inline]
760 fn get_field(&self, key: &str) -> Option<UdValue> {
761 self.inner().get_field(key)
762 }
763
764 #[inline]
765 fn set_field(&mut self, key: &str, value: UdValue) -> Option<Result<(), String>> {
766 self.inner_mut().set_field(key, value)
767 }
768
769 fn lua_tostring(&self) -> Option<String> {
770 self.inner().lua_tostring()
771 }
772
773 fn lua_eq(&self, other: &dyn UserDataTrait) -> Option<bool> {
774 self.inner().lua_eq(other)
775 }
776
777 fn lua_lt(&self, other: &dyn UserDataTrait) -> Option<bool> {
778 self.inner().lua_lt(other)
779 }
780
781 fn lua_le(&self, other: &dyn UserDataTrait) -> Option<bool> {
782 self.inner().lua_le(other)
783 }
784
785 fn lua_len(&self) -> Option<UdValue> {
786 self.inner().lua_len()
787 }
788
789 fn lua_unm(&self) -> Option<UdValue> {
790 self.inner().lua_unm()
791 }
792
793 fn lua_add(&self, other: &UdValue) -> Option<UdValue> {
794 self.inner().lua_add(other)
795 }
796
797 fn lua_sub(&self, other: &UdValue) -> Option<UdValue> {
798 self.inner().lua_sub(other)
799 }
800
801 fn lua_mul(&self, other: &UdValue) -> Option<UdValue> {
802 self.inner().lua_mul(other)
803 }
804
805 fn lua_div(&self, other: &UdValue) -> Option<UdValue> {
806 self.inner().lua_div(other)
807 }
808
809 fn lua_mod(&self, other: &UdValue) -> Option<UdValue> {
810 self.inner().lua_mod(other)
811 }
812
813 fn lua_concat(&self, other: &UdValue) -> Option<UdValue> {
814 self.inner().lua_concat(other)
815 }
816
817 fn lua_gc(&mut self) {
818 // Intentionally a no-op: we don't own the data, so GC must not drop it.
819 }
820
821 fn lua_close(&mut self) {
822 // Same: no-op for borrowed references.
823 }
824
825 fn field_names(&self) -> &'static [&'static str] {
826 self.inner().field_names()
827 }
828
829 fn as_any(&self) -> &dyn Any {
830 self.inner().as_any()
831 }
832
833 fn as_any_mut(&mut self) -> &mut dyn Any {
834 self.inner_mut().as_any_mut()
835 }
836}