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/// The Lua error type. Carries a `LuaValue` payload because Lua errors can
8/// be any value (typically a string).
9#[derive(Debug, Clone)]
10pub enum LuaError {
11    Runtime(LuaValue),
12    Syntax(LuaValue),
13    Memory,
14    Error,
15    Yield,
16    File,
17    Gc,
18}
19
20impl LuaError {
21    // ── Generic message constructors ─────────────────────────────────────
22    pub fn runtime(args: fmt::Arguments<'_>) -> Self {
23        LuaError::Runtime(LuaValue::Str(crate::gc::GcRef::new(
24            crate::string::LuaString::from_bytes(format!("{}", args).into_bytes()),
25        )))
26    }
27    pub fn syntax(args: fmt::Arguments<'_>) -> Self {
28        LuaError::Syntax(LuaValue::Str(crate::gc::GcRef::new(
29            crate::string::LuaString::from_bytes(format!("{}", args).into_bytes()),
30        )))
31    }
32    pub fn syntax_at(args: fmt::Arguments<'_>, source: &[u8], line: i32) -> Self {
33        LuaError::Syntax(LuaValue::Str(crate::gc::GcRef::new(
34            crate::string::LuaString::from_bytes(
35                format!("{}:{}: {}", String::from_utf8_lossy(source), line, args).into_bytes()
36            ),
37        )))
38    }
39    pub fn syntax_raw(msg: &[u8]) -> Self {
40        LuaError::Syntax(LuaValue::Str(crate::gc::GcRef::new(
41            crate::string::LuaString::from_bytes(msg.to_vec()),
42        )))
43    }
44
45    // ── Standard-shape constructors ──────────────────────────────────────
46    pub fn type_error(v: &LuaValue, op: &str) -> Self {
47        LuaError::runtime(format_args!("attempt to {} a {} value", op, v.type_name()))
48    }
49    pub fn call_error(v: &LuaValue) -> Self {
50        Self::type_error(v, "call")
51    }
52    pub fn concat_error(p1: &LuaValue, p2: &LuaValue) -> Self {
53        let bad = if matches!(p1, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_)) { p2 } else { p1 };
54        LuaError::runtime(format_args!("attempt to concatenate a {} value", bad.type_name()))
55    }
56    pub fn arith_error(p1: &LuaValue, p2: &LuaValue, _msg: &str) -> Self {
57        let bad = if matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) { p2 } else { p1 };
58        LuaError::runtime(format_args!("attempt to perform arithmetic on a {} value", bad.type_name()))
59    }
60    pub fn int_overflow(_p1: &LuaValue, _p2: &LuaValue) -> Self {
61        LuaError::runtime(format_args!("number has no integer representation"))
62    }
63    pub fn order_error(p1: &LuaValue, p2: &LuaValue) -> Self {
64        LuaError::runtime(format_args!("attempt to compare {} with {}", p1.type_name(), p2.type_name()))
65    }
66    pub fn for_error(v: &LuaValue, what: &str) -> Self {
67        LuaError::runtime(format_args!("bad 'for' {} (number expected, got {})", what, v.type_name()))
68    }
69    pub fn arg_error(narg: i32, msg: &str) -> Self {
70        LuaError::runtime(format_args!("bad argument #{} ({})", narg, msg))
71    }
72    pub fn type_arg_error(narg: i32, expected: &str, got: &LuaValue) -> Self {
73        LuaError::runtime(format_args!("bad argument #{} ({} expected, got {})", narg, expected, got.type_name()))
74    }
75
76    // ── Pass-through constructors ────────────────────────────────────────
77    pub fn from_value(v: LuaValue) -> Self {
78        // Special-case: the global "not enough memory" string becomes Memory.
79        // The real impl checks ptr equality against G(L)->memerrmsg.
80        LuaError::Runtime(v)
81    }
82    pub fn with_status(status: LuaStatus) -> Self {
83        match status {
84            LuaStatus::Ok        => LuaError::Error,
85            LuaStatus::Yield     => LuaError::Yield,
86            LuaStatus::ErrRun    => LuaError::Runtime(LuaValue::Nil),
87            LuaStatus::ErrSyntax => LuaError::Syntax(LuaValue::Nil),
88            LuaStatus::ErrMem    => LuaError::Memory,
89            LuaStatus::ErrErr    => LuaError::Error,
90            LuaStatus::ErrFile   => LuaError::File,
91            LuaStatus::ErrGc     => LuaError::Gc,
92        }
93    }
94
95    pub fn to_status(&self) -> LuaStatus {
96        match self {
97            LuaError::Runtime(_) => LuaStatus::ErrRun,
98            LuaError::Syntax(_)  => LuaStatus::ErrSyntax,
99            LuaError::Memory     => LuaStatus::ErrMem,
100            LuaError::Error      => LuaStatus::ErrErr,
101            LuaError::Yield      => LuaStatus::Yield,
102            LuaError::File       => LuaStatus::ErrFile,
103            LuaError::Gc         => LuaStatus::ErrGc,
104        }
105    }
106
107    pub fn into_value(self) -> LuaValue {
108        match self {
109            LuaError::Runtime(v) | LuaError::Syntax(v) => v,
110            _ => LuaValue::Nil,
111        }
112    }
113}
114
115impl fmt::Display for LuaError {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        write!(f, "{:?}", self)
118    }
119}
120impl std::error::Error for LuaError {}
121
122// ──────────────────────────────────────────────────────────────────────────────
123// PORT STATUS
124//   source:        n/a (Rust-native error enum; no C analogue)
125//   target_crate:  lua-types
126//   confidence:    high
127//   todos:         0
128//   port_notes:    0
129//   unsafe_blocks: 0
130//   notes:         LuaError + supporting variants. Pure Rust idiom (Result<T, LuaError>) replacing
131//                  C's setjmp/longjmp error propagation. No direct C-source mapping.
132// ──────────────────────────────────────────────────────────────────────────────