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. 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}