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   { matches!(self, LuaValue::Nil) }
59    pub fn is_falsy(&self) -> bool { matches!(self, LuaValue::Nil | LuaValue::Bool(false)) }
60    pub fn is_truthy(&self) -> bool { !self.is_falsy() }
61    pub fn is_collectable(&self) -> bool {
62        matches!(self,
63            LuaValue::Str(_) | LuaValue::Table(_) | LuaValue::Function(_) |
64            LuaValue::UserData(_) | LuaValue::Thread(_))
65    }
66
67    pub fn as_int(&self) -> Option<i64> {
68        match self { LuaValue::Int(i) => Some(*i), _ => None }
69    }
70    pub fn as_float(&self) -> Option<f64> {
71        match self { LuaValue::Float(f) => Some(*f), _ => None }
72    }
73    pub fn as_string(&self) -> Option<&GcRef<LuaString>> {
74        match self { LuaValue::Str(s) => Some(s), _ => None }
75    }
76    pub fn as_table(&self) -> Option<&GcRef<LuaTable>> {
77        match self { LuaValue::Table(t) => Some(t), _ => None }
78    }
79}
80
81impl Default for LuaValue {
82    fn default() -> Self { LuaValue::Nil }
83}
84
85impl PartialEq for LuaValue {
86    fn eq(&self, other: &Self) -> bool {
87        match (self, other) {
88            (LuaValue::Nil, LuaValue::Nil) => true,
89            (LuaValue::Bool(a), LuaValue::Bool(b)) => a == b,
90            (LuaValue::Int(a), LuaValue::Int(b)) => a == b,
91            (LuaValue::Float(a), LuaValue::Float(b)) => a == b,
92            (LuaValue::Str(a), LuaValue::Str(b)) => {
93                GcRef::ptr_eq(a, b) || (a.hash() == b.hash() && a.as_bytes() == b.as_bytes())
94            }
95            (LuaValue::Table(a), LuaValue::Table(b)) => GcRef::ptr_eq(a, b),
96            (LuaValue::Function(a), LuaValue::Function(b)) => closure_eq(a, b),
97            (LuaValue::UserData(a), LuaValue::UserData(b)) => GcRef::ptr_eq(a, b),
98            (LuaValue::LightUserData(a), LuaValue::LightUserData(b)) => a == b,
99            (LuaValue::Thread(a), LuaValue::Thread(b)) => GcRef::ptr_eq(a, b),
100            _ => false,
101        }
102    }
103}
104
105/// Float-to-integer rounding mode (matches C-Lua's F2Imod).
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum F2Imod {
108    Floor,
109    Ceil,
110    Round,
111}
112
113// LuaTable now lives in `crate::table` as the canonical array+hash
114// implementation. The variant signature stays `LuaValue::Table(GcRef<LuaTable>)`.
115
116fn closure_eq(a: &LuaClosure, b: &LuaClosure) -> bool {
117    match (a, b) {
118        (LuaClosure::Lua(x), LuaClosure::Lua(y)) => GcRef::ptr_eq(x, y),
119        (LuaClosure::C(x), LuaClosure::C(y)) => GcRef::ptr_eq(x, y),
120        (LuaClosure::LightC(x), LuaClosure::LightC(y)) => x == y,
121        _ => false,
122    }
123}
124
125/// Identity of a Lua thread (coroutine).
126///
127/// The real per-thread `LuaState` lives in `lua-vm` and is held by
128/// `GlobalState` keyed by this id. `LuaValue::Thread` carries a
129/// `GcRef<LuaThread>` so that pointer-equality of the wrapping `GcRef`
130/// still implements thread-identity comparison, but the only payload is
131/// the registry key — keeping `LuaState` outside `lua-types` avoids the
132/// `lua-types` → `lua-vm` crate cycle.
133///
134/// Convention: `id == 0` is reserved for the main thread. Coroutines are
135/// assigned ids starting at 1.
136#[derive(Debug)]
137pub struct LuaThread {
138    pub id: u64,
139}
140impl LuaThread {
141    pub fn new(id: u64) -> Self { LuaThread { id } }
142    pub fn placeholder() -> Self { LuaThread { id: 0 } }
143}
144
145// ──────────────────────────────────────────────────────────────────────────────
146// PORT STATUS
147//   source:        src/lobject.h (TValue, Value union, tags)
148//   target_crate:  lua-types
149//   confidence:    high
150//   todos:         0
151//   port_notes:    0
152//   unsafe_blocks: 0
153//   notes:         Canonical LuaValue tagged enum. C uses a {value, tag} struct with a
154//                  union of (gco/number/bool/light-userdata); we use a Rust enum
155//                  with each variant carrying its payload directly.
156// ──────────────────────────────────────────────────────────────────────────────