rust_lodash/utils/
error.rs

1/*!
2Error handling for Lodash-RS.
3
4This module provides comprehensive error types and handling for all operations
5in the Lodash-RS library.
6*/
7
8/// Main error type for Lodash-RS operations.
9#[derive(Debug, Clone, PartialEq)]
10pub enum LodashError {
11    /// Invalid input data
12    /// Invalid input provided to a function.
13    InvalidInput { 
14        /// The error message describing the invalid input.
15        message: String 
16    },
17
18    /// Type conversion error
19    /// Type conversion error.
20    TypeConversion { 
21        /// The source type that failed to convert.
22        from: String, 
23        /// The target type that was attempted.
24        to: String 
25    },
26
27    /// Index out of bounds
28    /// Index out of bounds error.
29    IndexOutOfBounds { 
30        /// The index that was out of bounds.
31        index: usize, 
32        /// The size of the collection.
33        size: usize 
34    },
35
36    /// Empty collection operation
37    EmptyCollection,
38
39    /// Invalid predicate function
40    /// Invalid predicate function error.
41    InvalidPredicate { 
42        /// The error message describing the invalid predicate.
43        message: String 
44    },
45
46    /// Async operation error
47    #[cfg(feature = "async")]
48    AsyncError { message: String },
49
50    /// Parallel operation error
51    #[cfg(feature = "parallel")]
52    ParallelError { message: String },
53
54    /// WASM operation error
55    #[cfg(feature = "wasm")]
56    WasmError { message: String },
57
58    /// Custom error from user-provided functions
59    /// Custom error with a specific message.
60    Custom { 
61        /// The custom error message.
62        message: String 
63    },
64}
65
66impl std::fmt::Display for LodashError {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        match self {
69            LodashError::InvalidInput { message } => {
70                write!(f, "Invalid input: {message}")
71            }
72            LodashError::TypeConversion { from, to } => {
73                write!(f, "Type conversion failed: {from} -> {to}")
74            }
75            LodashError::IndexOutOfBounds { index, size } => {
76                write!(f, "Index {index} is out of bounds for collection of size {size}")
77            }
78            LodashError::EmptyCollection => {
79                write!(f, "Operation requires non-empty collection")
80            }
81            LodashError::InvalidPredicate { message } => {
82                write!(f, "Invalid predicate function: {message}")
83            }
84            #[cfg(feature = "async")]
85            LodashError::AsyncError { message } => {
86                write!(f, "Async operation failed: {}", message)
87            }
88            #[cfg(feature = "parallel")]
89            LodashError::ParallelError { message } => {
90                write!(f, "Parallel operation failed: {}", message)
91            }
92            #[cfg(feature = "wasm")]
93            LodashError::WasmError { message } => {
94                write!(f, "WASM operation failed: {}", message)
95            }
96            LodashError::Custom { message } => {
97                write!(f, "Custom error: {message}")
98            }
99        }
100    }
101}
102
103impl std::error::Error for LodashError {}
104
105/// Result type alias for Lodash-RS operations.
106pub type Result<T> = std::result::Result<T, LodashError>;
107
108impl LodashError {
109    /// Create a new invalid input error.
110    pub fn invalid_input(message: impl Into<String>) -> Self {
111        Self::InvalidInput {
112            message: message.into(),
113        }
114    }
115
116    /// Create a new type conversion error.
117    pub fn type_conversion(from: impl Into<String>, to: impl Into<String>) -> Self {
118        Self::TypeConversion {
119            from: from.into(),
120            to: to.into(),
121        }
122    }
123
124    /// Create a new index out of bounds error.
125    #[must_use]
126    pub fn index_out_of_bounds(index: usize, size: usize) -> Self {
127        Self::IndexOutOfBounds { index, size }
128    }
129
130    /// Create a new empty collection error.
131    #[must_use]
132    pub fn empty_collection() -> Self {
133        Self::EmptyCollection
134    }
135
136    /// Create a new invalid predicate error.
137    pub fn invalid_predicate(message: impl Into<String>) -> Self {
138        Self::InvalidPredicate {
139            message: message.into(),
140        }
141    }
142
143    /// Create a new custom error.
144    pub fn custom(message: impl Into<String>) -> Self {
145        Self::Custom {
146            message: message.into(),
147        }
148    }
149
150    #[cfg(feature = "async")]
151    /// Create a new async error.
152    pub fn async_error(message: impl Into<String>) -> Self {
153        Self::AsyncError {
154            message: message.into(),
155        }
156    }
157
158    #[cfg(feature = "parallel")]
159    /// Create a new parallel error.
160    pub fn parallel_error(message: impl Into<String>) -> Self {
161        Self::ParallelError {
162            message: message.into(),
163        }
164    }
165
166    #[cfg(feature = "wasm")]
167    /// Create a new WASM error.
168    pub fn wasm_error(message: impl Into<String>) -> Self {
169        Self::WasmError {
170            message: message.into(),
171        }
172    }
173}
174
175/// Extension trait for converting other error types to `LodashError`.
176pub trait IntoLodashError<T> {
177    /// Convert the error to a `LodashError`.
178    /// 
179    /// # Errors
180    /// 
181    /// Returns a `LodashError` if the conversion fails.
182    fn into_lodash_error(self) -> Result<T>;
183}
184
185impl<T, E> IntoLodashError<T> for std::result::Result<T, E>
186where
187    E: std::fmt::Display,
188{
189    fn into_lodash_error(self) -> Result<T> {
190        self.map_err(|e| LodashError::custom(e.to_string()))
191    }
192}
193
194/// Macro for creating custom errors with context.
195#[macro_export]
196macro_rules! lodash_error {
197    ($variant:ident, $($arg:expr),*) => {
198        LodashError::$variant {
199            $($arg),*
200        }
201    };
202}
203
204/// Macro for early return on error with context.
205#[macro_export]
206macro_rules! lodash_try {
207    ($expr:expr) => {
208        match $expr {
209            Ok(val) => val,
210            Err(err) => return Err(err.into_lodash_error().unwrap_err()),
211        }
212    };
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218
219    #[test]
220    fn test_error_creation() {
221        let err = LodashError::invalid_input("test message");
222        assert!(matches!(err, LodashError::InvalidInput { .. }));
223        assert_eq!(err.to_string(), "Invalid input: test message");
224
225        let err = LodashError::type_conversion("String", "i32");
226        assert!(matches!(err, LodashError::TypeConversion { .. }));
227        assert_eq!(err.to_string(), "Type conversion failed: String -> i32");
228
229        let err = LodashError::index_out_of_bounds(5, 3);
230        assert!(matches!(err, LodashError::IndexOutOfBounds { .. }));
231        assert_eq!(err.to_string(), "Index 5 is out of bounds for collection of size 3");
232
233        let err = LodashError::empty_collection();
234        assert!(matches!(err, LodashError::EmptyCollection));
235        assert_eq!(err.to_string(), "Operation requires non-empty collection");
236    }
237
238    #[test]
239    fn test_error_conversion() {
240        let result: std::result::Result<i32, String> = Err("test error".to_string());
241        let lodash_result: Result<i32> = result.into_lodash_error();
242        
243        assert!(lodash_result.is_err());
244        if let Err(LodashError::Custom { message }) = lodash_result {
245            assert_eq!(message, "test error");
246        } else {
247            panic!("Expected Custom error variant");
248        }
249    }
250}