Skip to main content

lua_types/
error.rs

1//! `LuaError` and its canonical constructors. PORT_STRATEGY §3.7, PORTING.md §6.
2
3use crate::status::LuaStatus;
4use crate::value::LuaValue;
5use std::fmt;
6
7/// Internal control-flow payload used by the standalone CLI to implement
8/// `os.exit` without making Lua protected calls catch it as an ordinary error.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct LuaExit(pub i32);
11
12/// Internal control-flow payload for Lua 5.5 `coroutine.close()` self-close.
13/// It is caught at the coroutine resume boundary; panic hooks should suppress it
14/// like [`LuaExit`] because it is not a Rust runtime panic.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct LuaThreadClose(pub LuaStatus);
17
18/// The Lua error type. Carries a `LuaValue` payload because Lua errors can
19/// be any value (typically a string).
20#[derive(Debug, Clone)]
21pub enum LuaError {
22    Runtime(LuaValue),
23    Syntax(LuaValue),
24    Memory,
25    Error,
26    Yield,
27    File,
28    Gc,
29}
30
31impl LuaError {
32    // ── Generic message constructors ─────────────────────────────────────
33    pub fn runtime(args: fmt::Arguments<'_>) -> Self {
34        LuaError::Runtime(LuaValue::Str(crate::gc::GcRef::new(
35            crate::string::LuaString::from_bytes(format!("{}", args).into_bytes()),
36        )))
37    }
38    pub fn syntax(args: fmt::Arguments<'_>) -> Self {
39        LuaError::Syntax(LuaValue::Str(crate::gc::GcRef::new(
40            crate::string::LuaString::from_bytes(format!("{}", args).into_bytes()),
41        )))
42    }
43    pub fn syntax_at(args: fmt::Arguments<'_>, source: &[u8], line: i32) -> Self {
44        LuaError::Syntax(LuaValue::Str(crate::gc::GcRef::new(
45            crate::string::LuaString::from_bytes(
46                format!("{}:{}: {}", String::from_utf8_lossy(source), line, args).into_bytes(),
47            ),
48        )))
49    }
50    pub fn syntax_raw(msg: &[u8]) -> Self {
51        LuaError::Syntax(LuaValue::Str(crate::gc::GcRef::new(
52            crate::string::LuaString::from_bytes(msg.to_vec()),
53        )))
54    }
55
56    // ── Standard-shape constructors ──────────────────────────────────────
57    pub fn type_error(v: &LuaValue, op: &str) -> Self {
58        LuaError::runtime(format_args!("attempt to {} a {} value", op, v.type_name()))
59    }
60    pub fn call_error(v: &LuaValue) -> Self {
61        Self::type_error(v, "call")
62    }
63    pub fn concat_error(p1: &LuaValue, p2: &LuaValue) -> Self {
64        let bad = if matches!(p1, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_)) {
65            p2
66        } else {
67            p1
68        };
69        LuaError::runtime(format_args!(
70            "attempt to concatenate a {} value",
71            bad.type_name()
72        ))
73    }
74    pub fn arith_error(p1: &LuaValue, p2: &LuaValue, _msg: &str) -> Self {
75        let bad = if matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
76            p2
77        } else {
78            p1
79        };
80        LuaError::runtime(format_args!(
81            "attempt to perform arithmetic on a {} value",
82            bad.type_name()
83        ))
84    }
85    pub fn int_overflow(_p1: &LuaValue, _p2: &LuaValue) -> Self {
86        LuaError::runtime(format_args!("number has no integer representation"))
87    }
88    pub fn order_error(p1: &LuaValue, p2: &LuaValue) -> Self {
89        LuaError::runtime(format_args!(
90            "attempt to compare {} with {}",
91            p1.type_name(),
92            p2.type_name()
93        ))
94    }
95    pub fn for_error(v: &LuaValue, what: &str) -> Self {
96        LuaError::runtime(format_args!(
97            "bad 'for' {} (number expected, got {})",
98            what,
99            v.type_name()
100        ))
101    }
102    pub fn arg_error(narg: i32, msg: &str) -> Self {
103        LuaError::runtime(format_args!("bad argument #{} ({})", narg, msg))
104    }
105    pub fn type_arg_error(narg: i32, expected: &str, got: &LuaValue) -> Self {
106        LuaError::runtime(format_args!(
107            "bad argument #{} ({} expected, got {})",
108            narg,
109            expected,
110            got.type_name()
111        ))
112    }
113
114    // ── Pass-through constructors ────────────────────────────────────────
115    pub fn from_value(v: LuaValue) -> Self {
116        // Special-case: the global "not enough memory" string becomes Memory.
117        // The real impl checks ptr equality against G(L)->memerrmsg.
118        LuaError::Runtime(v)
119    }
120    pub fn with_status(status: LuaStatus) -> Self {
121        match status {
122            LuaStatus::Ok => LuaError::Error,
123            LuaStatus::Yield => LuaError::Yield,
124            LuaStatus::ErrRun => LuaError::Runtime(LuaValue::Nil),
125            LuaStatus::ErrSyntax => LuaError::Syntax(LuaValue::Nil),
126            LuaStatus::ErrMem => LuaError::Memory,
127            LuaStatus::ErrErr => LuaError::Error,
128            LuaStatus::ErrFile => LuaError::File,
129            LuaStatus::ErrGc => LuaError::Gc,
130        }
131    }
132
133    pub fn to_status(&self) -> LuaStatus {
134        match self {
135            LuaError::Runtime(_) => LuaStatus::ErrRun,
136            LuaError::Syntax(_) => LuaStatus::ErrSyntax,
137            LuaError::Memory => LuaStatus::ErrMem,
138            LuaError::Error => LuaStatus::ErrErr,
139            LuaError::Yield => LuaStatus::Yield,
140            LuaError::File => LuaStatus::ErrFile,
141            LuaError::Gc => LuaStatus::ErrGc,
142        }
143    }
144
145    pub fn into_value(self) -> LuaValue {
146        match self {
147            LuaError::Runtime(v) | LuaError::Syntax(v) => v,
148            _ => LuaValue::Nil,
149        }
150    }
151}
152
153impl fmt::Display for LuaError {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        write!(f, "{:?}", self)
156    }
157}
158impl std::error::Error for LuaError {}
159
160// ──────────────────────────────────────────────────────────────────────────────
161// PORT STATUS
162//   source:        n/a (Rust-native error enum; no C analogue)
163//   target_crate:  lua-types
164//   confidence:    high
165//   todos:         0
166//   port_notes:    0
167//   unsafe_blocks: 0
168//   notes:         LuaError + supporting variants. Pure Rust idiom (Result<T, LuaError>) replacing
169//                  C's setjmp/longjmp error propagation. No direct C-source mapping.
170// ──────────────────────────────────────────────────────────────────────────────