Skip to main content

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