1use std::fmt;
4
5use uika_ffi::UikaErrorCode;
6
7#[derive(Debug)]
9pub enum UikaError {
10 ObjectDestroyed,
11 InvalidCast,
12 PropertyNotFound(String),
13 FunctionNotFound(String),
14 TypeMismatch,
15 NullArgument,
16 IndexOutOfRange,
17 InvalidOperation(String),
18 Internal(String),
19 BufferTooSmall,
20}
21
22impl fmt::Display for UikaError {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 UikaError::ObjectDestroyed => write!(f, "object has been destroyed"),
26 UikaError::InvalidCast => write!(f, "invalid cast"),
27 UikaError::PropertyNotFound(name) => write!(f, "property not found: {name}"),
28 UikaError::FunctionNotFound(name) => write!(f, "function not found: {name}"),
29 UikaError::TypeMismatch => write!(f, "type mismatch"),
30 UikaError::NullArgument => write!(f, "null argument"),
31 UikaError::IndexOutOfRange => write!(f, "index out of range"),
32 UikaError::InvalidOperation(msg) => write!(f, "invalid operation: {msg}"),
33 UikaError::Internal(msg) => write!(f, "internal error: {msg}"),
34 UikaError::BufferTooSmall => write!(f, "buffer too small"),
35 }
36 }
37}
38
39impl std::error::Error for UikaError {}
40
41pub type UikaResult<T> = Result<T, UikaError>;
43
44pub fn check_ffi(code: UikaErrorCode) -> UikaResult<()> {
47 match code {
48 UikaErrorCode::Ok => Ok(()),
49 other => Err(UikaError::from(other)),
50 }
51}
52
53pub fn check_ffi_ctx(code: UikaErrorCode, context: &str) -> UikaResult<()> {
55 match code {
56 UikaErrorCode::Ok => Ok(()),
57 UikaErrorCode::PropertyNotFound => Err(UikaError::PropertyNotFound(context.into())),
58 UikaErrorCode::FunctionNotFound => Err(UikaError::FunctionNotFound(context.into())),
59 UikaErrorCode::InvalidOperation => Err(UikaError::InvalidOperation(context.into())),
60 other => Err(UikaError::from(other)),
61 }
62}
63
64#[inline(always)]
68pub fn ffi_infallible(code: UikaErrorCode) {
69 debug_assert_eq!(
70 code,
71 UikaErrorCode::Ok,
72 "FFI call returned {:?} after pre-validation",
73 code
74 );
75}
76
77#[inline(always)]
79pub fn ffi_infallible_ctx(code: UikaErrorCode, ctx: &str) {
80 debug_assert_eq!(
81 code,
82 UikaErrorCode::Ok,
83 "FFI '{}' returned {:?} after pre-validation",
84 ctx,
85 code
86 );
87}
88
89impl From<UikaErrorCode> for UikaError {
90 #[allow(clippy::match_same_arms)]
91 fn from(code: UikaErrorCode) -> Self {
92 match code {
93 UikaErrorCode::Ok => {
94 UikaError::Internal("unexpected Ok error code".into())
97 }
98 UikaErrorCode::ObjectDestroyed => UikaError::ObjectDestroyed,
99 UikaErrorCode::InvalidCast => UikaError::InvalidCast,
100 UikaErrorCode::PropertyNotFound => UikaError::PropertyNotFound(String::new()),
101 UikaErrorCode::FunctionNotFound => UikaError::FunctionNotFound(String::new()),
102 UikaErrorCode::TypeMismatch => UikaError::TypeMismatch,
103 UikaErrorCode::NullArgument => UikaError::NullArgument,
104 UikaErrorCode::IndexOutOfRange => UikaError::IndexOutOfRange,
105 UikaErrorCode::InvalidOperation => UikaError::InvalidOperation(String::new()),
106 UikaErrorCode::InternalError => UikaError::Internal(String::new()),
107 UikaErrorCode::BufferTooSmall => UikaError::BufferTooSmall,
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn check_ffi_ok_returns_ok() {
118 assert!(check_ffi(UikaErrorCode::Ok).is_ok());
119 }
120
121 #[test]
122 fn check_ffi_errors_map_correctly() {
123 let cases = [
124 (UikaErrorCode::ObjectDestroyed, "ObjectDestroyed"),
125 (UikaErrorCode::InvalidCast, "InvalidCast"),
126 (UikaErrorCode::PropertyNotFound, "PropertyNotFound"),
127 (UikaErrorCode::FunctionNotFound, "FunctionNotFound"),
128 (UikaErrorCode::TypeMismatch, "TypeMismatch"),
129 (UikaErrorCode::NullArgument, "NullArgument"),
130 (UikaErrorCode::IndexOutOfRange, "IndexOutOfRange"),
131 (UikaErrorCode::InvalidOperation, "InvalidOperation"),
132 (UikaErrorCode::InternalError, "Internal"),
133 ];
134 for (code, expected_variant) in cases {
135 let err = check_ffi(code).unwrap_err();
136 let debug = format!("{err:?}");
137 assert!(
138 debug.starts_with(expected_variant),
139 "expected {expected_variant}, got {debug}"
140 );
141 }
142 }
143
144 #[test]
145 fn display_formats_are_human_readable() {
146 let err = UikaError::PropertyNotFound("Health".into());
147 assert_eq!(err.to_string(), "property not found: Health");
148 }
149}