spinoso_exception/core/
uncaughtthrowerror.rs1use alloc::borrow::Cow;
4use alloc::string::String;
5use alloc::vec::Vec;
6use core::error;
7use core::fmt;
8
9use bstr::ByteSlice;
10use scolapasta_string_escape::format_debug_escape_into;
11
12use crate::RubyException;
13
14#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
27pub struct UncaughtThrowError {
28    message: Cow<'static, [u8]>,
29}
30
31impl fmt::Debug for UncaughtThrowError {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        f.debug_struct("UncaughtThrowError")
34            .field("message", &self.message().as_bstr())
35            .finish()
36    }
37}
38
39impl UncaughtThrowError {
40    #[inline]
52    #[must_use]
53    pub const fn new() -> Self {
54        const DEFAULT_MESSAGE: &[u8] = b"UncaughtThrowError";
55
56        let message = Cow::Borrowed(DEFAULT_MESSAGE);
60        Self { message }
61    }
62
63    #[inline]
74    #[must_use]
75    pub const fn with_message(message: &'static str) -> Self {
76        let message = Cow::Borrowed(message.as_bytes());
77        Self { message }
78    }
79
80    #[inline]
92    #[must_use]
93    pub fn message(&self) -> &[u8] {
94        self.message.as_ref()
95    }
96
97    #[inline]
107    #[must_use]
108    pub const fn name(&self) -> &'static str {
109        "UncaughtThrowError"
110    }
111}
112
113impl From<String> for UncaughtThrowError {
114    #[inline]
115    fn from(message: String) -> Self {
116        let message = Cow::Owned(message.into_bytes());
117        Self { message }
118    }
119}
120
121impl From<&'static str> for UncaughtThrowError {
122    #[inline]
123    fn from(message: &'static str) -> Self {
124        let message = Cow::Borrowed(message.as_bytes());
125        Self { message }
126    }
127}
128
129impl From<Cow<'static, str>> for UncaughtThrowError {
130    #[inline]
131    fn from(message: Cow<'static, str>) -> Self {
132        let message = match message {
133            Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
134            Cow::Owned(s) => Cow::Owned(s.into_bytes()),
135        };
136        Self { message }
137    }
138}
139
140impl From<Vec<u8>> for UncaughtThrowError {
141    #[inline]
142    fn from(message: Vec<u8>) -> Self {
143        let message = Cow::Owned(message);
144        Self { message }
145    }
146}
147
148impl From<&'static [u8]> for UncaughtThrowError {
149    #[inline]
150    fn from(message: &'static [u8]) -> Self {
151        let message = Cow::Borrowed(message);
152        Self { message }
153    }
154}
155
156impl From<Cow<'static, [u8]>> for UncaughtThrowError {
157    #[inline]
158    fn from(message: Cow<'static, [u8]>) -> Self {
159        Self { message }
160    }
161}
162
163impl fmt::Display for UncaughtThrowError {
164    #[inline]
165    fn fmt(&self, mut f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        f.write_str(self.name())?;
167        f.write_str(" (")?;
168        let message = self.message.as_ref();
169        format_debug_escape_into(&mut f, message)?;
170        f.write_str(")")?;
171        Ok(())
172    }
173}
174
175impl error::Error for UncaughtThrowError {}
176
177impl RubyException for UncaughtThrowError {
178    #[inline]
179    fn message(&self) -> Cow<'_, [u8]> {
180        Cow::Borrowed(Self::message(self))
181    }
182
183    #[inline]
184    fn name(&self) -> Cow<'_, str> {
185        Cow::Borrowed(Self::name(self))
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use alloc::borrow::Cow;
192    use alloc::format;
193    use alloc::string::ToString;
194    use core::cmp::Ordering;
195    use core::error;
196
197    use bstr::ByteSlice;
198
199    use super::*;
200
201    #[test]
202    fn test_new() {
203        let exception = UncaughtThrowError::new();
204        assert_eq!(exception.message().as_bstr(), b"UncaughtThrowError".as_bstr());
205        assert_eq!(exception.name(), "UncaughtThrowError");
206    }
207
208    #[test]
209    fn test_with_message() {
210        let custom_message = "custom message";
211        let exception = UncaughtThrowError::with_message(custom_message);
212        assert_eq!(exception.message().as_bstr(), custom_message.as_bytes().as_bstr());
213        assert_eq!(exception.name(), "UncaughtThrowError");
215    }
216
217    #[test]
218    fn test_from_string() {
219        let message = "from String".to_string();
220        let exception: UncaughtThrowError = message.into();
221        assert_eq!(exception.message().as_bstr(), b"from String".as_bstr());
222    }
223
224    #[test]
225    fn test_from_static_str() {
226        let message: &'static str = "from &'static str";
227        let exception: UncaughtThrowError = message.into();
228        assert_eq!(exception.message().as_bstr(), b"from &'static str".as_bstr());
229    }
230
231    #[test]
232    fn test_from_cow_str_borrowed() {
233        let cow: Cow<'static, str> = Cow::Borrowed("from Cow borrowed");
234        let exception: UncaughtThrowError = cow.into();
235        assert_eq!(exception.message().as_bstr(), b"from Cow borrowed".as_bstr());
236    }
237
238    #[test]
239    fn test_from_cow_str_owned() {
240        let cow: Cow<'static, str> = Cow::Owned("from Cow owned".to_string());
241        let exception: UncaughtThrowError = cow.into();
242        assert_eq!(exception.message().as_bstr(), b"from Cow owned".as_bstr());
243    }
244
245    #[test]
246    fn test_from_vec_u8() {
247        let vec = b"from Vec<u8>".to_vec();
248        let exception: UncaughtThrowError = vec.into();
249        assert_eq!(exception.message().as_bstr(), b"from Vec<u8>".as_bstr());
250    }
251
252    #[test]
253    fn test_from_static_slice() {
254        let slice: &'static [u8] = b"from &'static [u8]";
255        let exception: UncaughtThrowError = slice.into();
256        assert_eq!(exception.message().as_bstr(), b"from &'static [u8]".as_bstr());
257    }
258
259    #[test]
260    fn test_from_cow_u8_borrowed() {
261        let cow: Cow<'static, [u8]> = Cow::Borrowed(b"from Cow<u8> borrowed");
262        let exception: UncaughtThrowError = cow.into();
263        assert_eq!(exception.message().as_bstr(), b"from Cow<u8> borrowed".as_bstr());
264    }
265
266    #[test]
267    fn test_from_cow_u8_owned() {
268        let cow: Cow<'static, [u8]> = Cow::Owned(b"from Cow<u8> owned".to_vec());
269        let exception: UncaughtThrowError = cow.into();
270        assert_eq!(exception.message().as_bstr(), b"from Cow<u8> owned".as_bstr());
271    }
272
273    #[test]
274    fn test_debug() {
275        let exception = UncaughtThrowError::with_message("display test");
276        let output = format!("{exception:?}");
277        assert!(output.contains("UncaughtThrowError"));
279        assert!(output.contains("display test"));
280        assert!(output.starts_with("UncaughtThrowError {"));
282        assert!(output.ends_with('}'));
283    }
284
285    #[test]
286    fn test_display() {
287        let exception = UncaughtThrowError::with_message("display test");
288        let output = format!("{exception}");
289        assert!(output.contains("UncaughtThrowError"));
291        assert!(output.contains("display test"));
292        assert!(output.starts_with("UncaughtThrowError ("));
294        assert!(output.ends_with(')'));
295    }
296
297    #[test]
298    fn test_error_trait() {
299        let exception = UncaughtThrowError::with_message("error trait test");
300        let error_obj: &dyn error::Error = &exception;
301        assert!(error_obj.source().is_none());
303        assert_eq!(error_obj.to_string(), format!("{exception}"));
305    }
306
307    #[test]
308    fn test_ruby_exception_trait() {
309        let exception = UncaughtThrowError::with_message("ruby trait test");
310        let msg = RubyException::message(&exception);
311        let name = RubyException::name(&exception);
312        assert_eq!(msg.as_bstr(), exception.message().as_bstr());
313        assert_eq!(name, exception.name());
314    }
315
316    #[test]
317    fn test_default() {
318        let default_error = UncaughtThrowError::default();
319        assert_eq!(default_error.message(), b"");
321        assert_eq!(default_error.name(), "UncaughtThrowError");
323    }
324
325    #[test]
326    fn test_clone() {
327        let original = UncaughtThrowError::with_message("clone test");
328        let cloned = original.clone();
329        assert_eq!(original, cloned, "Cloned error should be equal to the original");
330    }
331
332    #[test]
333    fn test_partial_eq() {
334        let error_a = UncaughtThrowError::with_message("test message");
335        let error_b = UncaughtThrowError::with_message("test message");
336        let error_c = UncaughtThrowError::with_message("different message");
337        assert_eq!(error_a, error_b, "Errors with the same message should be equal");
338        assert_ne!(error_a, error_c, "Errors with different messages should not be equal");
339    }
340
341    #[test]
342    fn test_partial_ord() {
343        let error_a = UncaughtThrowError::with_message("aaa");
344        let error_b = UncaughtThrowError::with_message("bbb");
345        assert_eq!(error_a.partial_cmp(&error_a), Some(Ordering::Equal));
347        assert_eq!(error_a.partial_cmp(&error_b), Some(Ordering::Less));
349        assert_eq!(error_b.partial_cmp(&error_a), Some(Ordering::Greater));
350    }
351
352    #[test]
353    fn test_ord() {
354        let error_a = UncaughtThrowError::with_message("aaa");
355        let error_b = UncaughtThrowError::with_message("bbb");
356        assert_eq!(error_a.cmp(&error_a), Ordering::Equal);
358        assert_eq!(error_a.cmp(&error_b), Ordering::Less);
360        assert_eq!(error_b.cmp(&error_a), Ordering::Greater);
361    }
362}