Skip to main content

lua_types/
value.rs

1//! `LuaValue` — the tagged-union value type. PORT_STRATEGY §3.2.
2
3use crate::closure::LuaClosure;
4use crate::gc::GcRef;
5use crate::string::LuaString;
6use crate::userdata::LuaUserData;
7use std::ffi::c_void;
8
9pub use crate::table::LuaTable;
10
11/// The dynamically-typed Lua value. Replaces C's `TValue`.
12#[derive(Debug, Clone, Copy)]
13pub enum LuaValue {
14    Nil,
15    Bool(bool),
16    Int(i64),
17    Float(f64),
18    Str(GcRef<LuaString>),
19    Table(GcRef<LuaTable>),
20    Function(LuaClosure),
21    UserData(GcRef<LuaUserData>),
22    LightUserData(*mut c_void),
23    Thread(GcRef<LuaThread>),
24}
25
26impl LuaValue {
27    pub fn type_tag(&self) -> crate::LuaType {
28        use crate::LuaType::*;
29        match self {
30            LuaValue::Nil => Nil,
31            LuaValue::Bool(_) => Boolean,
32            LuaValue::Int(_) => Number,
33            LuaValue::Float(_) => Number,
34            LuaValue::Str(_) => String,
35            LuaValue::Table(_) => Table,
36            LuaValue::Function(_) => Function,
37            LuaValue::UserData(_) => UserData,
38            LuaValue::LightUserData(_) => LightUserData,
39            LuaValue::Thread(_) => Thread,
40        }
41    }
42
43    pub fn type_name(&self) -> &'static str {
44        match self {
45            LuaValue::Nil => "nil",
46            LuaValue::Bool(_) => "boolean",
47            LuaValue::Int(_) => "number",
48            LuaValue::Float(_) => "number",
49            LuaValue::Str(_) => "string",
50            LuaValue::Table(_) => "table",
51            LuaValue::Function(_) => "function",
52            LuaValue::UserData(_) => "userdata",
53            LuaValue::LightUserData(_) => "userdata",
54            LuaValue::Thread(_) => "thread",
55        }
56    }
57
58    pub fn is_nil(&self) -> bool {
59        matches!(self, LuaValue::Nil)
60    }
61    pub fn is_falsy(&self) -> bool {
62        matches!(self, LuaValue::Nil | LuaValue::Bool(false))
63    }
64    pub fn is_truthy(&self) -> bool {
65        !self.is_falsy()
66    }
67    pub fn is_collectable(&self) -> bool {
68        matches!(
69            self,
70            LuaValue::Str(_)
71                | LuaValue::Table(_)
72                | LuaValue::Function(_)
73                | LuaValue::UserData(_)
74                | LuaValue::Thread(_)
75        )
76    }
77
78    pub fn as_int(&self) -> Option<i64> {
79        match self {
80            LuaValue::Int(i) => Some(*i),
81            _ => None,
82        }
83    }
84    pub fn as_float(&self) -> Option<f64> {
85        match self {
86            LuaValue::Float(f) => Some(*f),
87            _ => None,
88        }
89    }
90    pub fn as_string(&self) -> Option<&GcRef<LuaString>> {
91        match self {
92            LuaValue::Str(s) => Some(s),
93            _ => None,
94        }
95    }
96    pub fn as_table(&self) -> Option<&GcRef<LuaTable>> {
97        match self {
98            LuaValue::Table(t) => Some(t),
99            _ => None,
100        }
101    }
102}
103
104impl Default for LuaValue {
105    fn default() -> Self {
106        LuaValue::Nil
107    }
108}
109
110impl PartialEq for LuaValue {
111    fn eq(&self, other: &Self) -> bool {
112        match (self, other) {
113            (LuaValue::Nil, LuaValue::Nil) => true,
114            (LuaValue::Bool(a), LuaValue::Bool(b)) => a == b,
115            (LuaValue::Int(a), LuaValue::Int(b)) => a == b,
116            (LuaValue::Float(a), LuaValue::Float(b)) => a == b,
117            (LuaValue::Str(a), LuaValue::Str(b)) => {
118                GcRef::ptr_eq(a, b) || (a.hash() == b.hash() && a.as_bytes() == b.as_bytes())
119            }
120            (LuaValue::Table(a), LuaValue::Table(b)) => GcRef::ptr_eq(a, b),
121            (LuaValue::Function(a), LuaValue::Function(b)) => closure_eq(a, b),
122            (LuaValue::UserData(a), LuaValue::UserData(b)) => GcRef::ptr_eq(a, b),
123            (LuaValue::LightUserData(a), LuaValue::LightUserData(b)) => a == b,
124            (LuaValue::Thread(a), LuaValue::Thread(b)) => GcRef::ptr_eq(a, b),
125            _ => false,
126        }
127    }
128}
129
130/// Float-to-integer rounding mode (matches C-Lua's F2Imod).
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub enum F2Imod {
133    Floor,
134    Ceil,
135    Round,
136}
137
138// LuaTable now lives in `crate::table` as the canonical array+hash
139// implementation. The variant signature stays `LuaValue::Table(GcRef<LuaTable>)`.
140
141fn closure_eq(a: &LuaClosure, b: &LuaClosure) -> bool {
142    match (a, b) {
143        (LuaClosure::Lua(x), LuaClosure::Lua(y)) => GcRef::ptr_eq(x, y),
144        (LuaClosure::C(x), LuaClosure::C(y)) => GcRef::ptr_eq(x, y),
145        (LuaClosure::LightC(x), LuaClosure::LightC(y)) => x == y,
146        _ => false,
147    }
148}
149
150/// Identity of a Lua thread (coroutine).
151///
152/// The real per-thread `LuaState` lives in `lua-vm` and is held by
153/// `GlobalState` keyed by this id. `LuaValue::Thread` carries a
154/// `GcRef<LuaThread>` so that pointer-equality of the wrapping `GcRef`
155/// still implements thread-identity comparison, but the only payload is
156/// the registry key — keeping `LuaState` outside `lua-types` avoids the
157/// `lua-types` → `lua-vm` crate cycle.
158///
159/// Convention: `id == 0` is reserved for the main thread. Coroutines are
160/// assigned ids starting at 1.
161#[derive(Debug)]
162pub struct LuaThread {
163    pub id: u64,
164}
165impl LuaThread {
166    pub fn new(id: u64) -> Self {
167        LuaThread { id }
168    }
169    pub fn placeholder() -> Self {
170        LuaThread { id: 0 }
171    }
172}
173
174// ──────────────────────────────────────────────────────────────────────────────
175// PORT STATUS
176//   source:        src/lobject.h (TValue, Value union, tags)
177//   target_crate:  lua-types
178//   confidence:    high
179//   todos:         0
180//   port_notes:    0
181//   unsafe_blocks: 0
182//   notes:         Canonical LuaValue tagged enum. C uses a {value, tag} struct with a
183//                  union of (gco/number/bool/light-userdata); we use a Rust enum
184//                  with each variant carrying its payload directly.
185// ──────────────────────────────────────────────────────────────────────────────