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)) => GcRef::ptr_eq(a, b) || a.as_bytes() == b.as_bytes(),
93            (LuaValue::Table(a), LuaValue::Table(b)) => GcRef::ptr_eq(a, b),
94            (LuaValue::Function(a), LuaValue::Function(b)) => closure_eq(a, b),
95            (LuaValue::UserData(a), LuaValue::UserData(b)) => GcRef::ptr_eq(a, b),
96            (LuaValue::LightUserData(a), LuaValue::LightUserData(b)) => a == b,
97            (LuaValue::Thread(a), LuaValue::Thread(b)) => GcRef::ptr_eq(a, b),
98            _ => false,
99        }
100    }
101}
102
103/// Float-to-integer rounding mode (matches C-Lua's F2Imod).
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub enum F2Imod {
106    Floor,
107    Ceil,
108    Round,
109}
110
111// LuaTable now lives in `crate::table` as the canonical array+hash
112// implementation. The variant signature stays `LuaValue::Table(GcRef<LuaTable>)`.
113
114fn closure_eq(a: &LuaClosure, b: &LuaClosure) -> bool {
115    match (a, b) {
116        (LuaClosure::Lua(x), LuaClosure::Lua(y)) => GcRef::ptr_eq(x, y),
117        (LuaClosure::C(x), LuaClosure::C(y)) => GcRef::ptr_eq(x, y),
118        (LuaClosure::LightC(x), LuaClosure::LightC(y)) => x == y,
119        _ => false,
120    }
121}
122
123/// Identity of a Lua thread (coroutine).
124///
125/// The real per-thread `LuaState` lives in `lua-vm` and is held by
126/// `GlobalState` keyed by this id. `LuaValue::Thread` carries a
127/// `GcRef<LuaThread>` so that pointer-equality of the wrapping `GcRef`
128/// still implements thread-identity comparison, but the only payload is
129/// the registry key — keeping `LuaState` outside `lua-types` avoids the
130/// `lua-types` → `lua-vm` crate cycle.
131///
132/// Convention: `id == 0` is reserved for the main thread. Coroutines are
133/// assigned ids starting at 1.
134#[derive(Debug)]
135pub struct LuaThread {
136    pub id: u64,
137}
138impl LuaThread {
139    pub fn new(id: u64) -> Self { LuaThread { id } }
140    pub fn placeholder() -> Self { LuaThread { id: 0 } }
141}
142
143// ──────────────────────────────────────────────────────────────────────────────
144// PORT STATUS
145//   source:        src/lobject.h (TValue, Value union, tags)
146//   target_crate:  lua-types
147//   confidence:    high
148//   todos:         0
149//   port_notes:    0
150//   unsafe_blocks: 0
151//   notes:         Canonical LuaValue tagged enum. C uses a {value, tag} struct with a
152//                  union of (gco/number/bool/light-userdata); we use a Rust enum
153//                  with each variant carrying its payload directly.
154// ──────────────────────────────────────────────────────────────────────────────