spinoso_exception/core/
syntaxerror.rs

1// @generated
2
3use 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/// Ruby `SyntaxError` error type.
15///
16/// Descendants of class [`Exception`] are used to communicate between
17/// [`Kernel#raise`] and `rescue` statements in `begin ... end` blocks.
18/// Exception objects carry information about the exception – its type (the
19/// exception's class name), an optional descriptive string, and optional
20/// traceback information. `Exception` subclasses may add additional information
21/// like [`NameError#name`].
22///
23/// [`Exception`]: https://ruby-doc.org/core-3.1.2/Exception.html
24/// [`Kernel#raise`]: https://ruby-doc.org/core-3.1.2/Kernel.html#method-i-raise
25/// [`NameError#name`]: https://ruby-doc.org/core-3.1.2/NameError.html#method-i-name
26#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
27pub struct SyntaxError {
28    message: Cow<'static, [u8]>,
29}
30
31impl fmt::Debug for SyntaxError {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        f.debug_struct("SyntaxError")
34            .field("message", &self.message().as_bstr())
35            .finish()
36    }
37}
38
39impl SyntaxError {
40    /// Construct a new, default `SyntaxError` Ruby exception.
41    ///
42    /// This constructor sets the exception message to `SyntaxError`.
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// # use spinoso_exception::*;
48    /// let exception = SyntaxError::new();
49    /// assert_eq!(exception.message(), b"SyntaxError");
50    /// ```
51    #[inline]
52    #[must_use]
53    pub const fn new() -> Self {
54        const DEFAULT_MESSAGE: &[u8] = b"SyntaxError";
55
56        // `Exception` objects initialized via (for example)
57        // `raise RuntimeError` or `RuntimeError.new` have `message`
58        // equal to the exception's class name.
59        let message = Cow::Borrowed(DEFAULT_MESSAGE);
60        Self { message }
61    }
62
63    /// Construct a new, `SyntaxError` Ruby exception with the given
64    /// message.
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// # use spinoso_exception::*;
70    /// let exception = SyntaxError::with_message("an error occurred");
71    /// assert_eq!(exception.message(), b"an error occurred");
72    /// ```
73    #[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    /// Return the message this Ruby exception was constructed with.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// # use spinoso_exception::*;
86    /// let exception = SyntaxError::new();
87    /// assert_eq!(exception.message(), b"SyntaxError");
88    /// let exception = SyntaxError::from("something went wrong");
89    /// assert_eq!(exception.message(), b"something went wrong");
90    /// ```
91    #[inline]
92    #[must_use]
93    pub fn message(&self) -> &[u8] {
94        self.message.as_ref()
95    }
96
97    /// Return this Ruby exception's class name.
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// # use spinoso_exception::*;
103    /// let exception = SyntaxError::new();
104    /// assert_eq!(exception.name(), "SyntaxError");
105    /// ```
106    #[inline]
107    #[must_use]
108    pub const fn name(&self) -> &'static str {
109        "SyntaxError"
110    }
111}
112
113impl From<String> for SyntaxError {
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 SyntaxError {
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 SyntaxError {
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 SyntaxError {
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 SyntaxError {
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 SyntaxError {
157    #[inline]
158    fn from(message: Cow<'static, [u8]>) -> Self {
159        Self { message }
160    }
161}
162
163impl fmt::Display for SyntaxError {
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 SyntaxError {}
176
177impl RubyException for SyntaxError {
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 = SyntaxError::new();
204        assert_eq!(exception.message().as_bstr(), b"SyntaxError".as_bstr());
205        assert_eq!(exception.name(), "SyntaxError");
206    }
207
208    #[test]
209    fn test_with_message() {
210        let custom_message = "custom message";
211        let exception = SyntaxError::with_message(custom_message);
212        assert_eq!(exception.message().as_bstr(), custom_message.as_bytes().as_bstr());
213        // The exception name remains the default even when a custom message is used.
214        assert_eq!(exception.name(), "SyntaxError");
215    }
216
217    #[test]
218    fn test_from_string() {
219        let message = "from String".to_string();
220        let exception: SyntaxError = 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: SyntaxError = 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: SyntaxError = 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: SyntaxError = 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: SyntaxError = 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: SyntaxError = 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: SyntaxError = 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: SyntaxError = 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 = SyntaxError::with_message("display test");
276        let output = format!("{exception:?}");
277        // Expected to contain the exception name and the debug-escaped message.
278        assert!(output.contains("SyntaxError"));
279        assert!(output.contains("display test"));
280        // Check the surrounding formatting.
281        assert!(output.starts_with("SyntaxError {"));
282        assert!(output.ends_with('}'));
283    }
284
285    #[test]
286    fn test_display() {
287        let exception = SyntaxError::with_message("display test");
288        let output = format!("{exception}");
289        // Expected to contain the exception name and the debug-escaped message.
290        assert!(output.contains("SyntaxError"));
291        assert!(output.contains("display test"));
292        // Check the surrounding formatting.
293        assert!(output.starts_with("SyntaxError ("));
294        assert!(output.ends_with(')'));
295    }
296
297    #[test]
298    fn test_error_trait() {
299        let exception = SyntaxError::with_message("error trait test");
300        let error_obj: &dyn error::Error = &exception;
301        // Our implementation does not provide a source.
302        assert!(error_obj.source().is_none());
303        // to_string() uses Display.
304        assert_eq!(error_obj.to_string(), format!("{exception}"));
305    }
306
307    #[test]
308    fn test_ruby_exception_trait() {
309        let exception = SyntaxError::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 = SyntaxError::default();
319        // By default, the message should be an empty slice since Default is auto-derived.
320        assert_eq!(default_error.message(), b"");
321        // The name method always returns the constant exception name.
322        assert_eq!(default_error.name(), "SyntaxError");
323    }
324
325    #[test]
326    fn test_clone() {
327        let original = SyntaxError::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 = SyntaxError::with_message("test message");
335        let error_b = SyntaxError::with_message("test message");
336        let error_c = SyntaxError::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 = SyntaxError::with_message("aaa");
344        let error_b = SyntaxError::with_message("bbb");
345        // Same error should compare equal.
346        assert_eq!(error_a.partial_cmp(&error_a), Some(Ordering::Equal));
347        // Lexicographic ordering of the underlying messages.
348        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 = SyntaxError::with_message("aaa");
355        let error_b = SyntaxError::with_message("bbb");
356        // Same error should compare equal.
357        assert_eq!(error_a.cmp(&error_a), Ordering::Equal);
358        // Lexicographic ordering of the underlying messages.
359        assert_eq!(error_a.cmp(&error_b), Ordering::Less);
360        assert_eq!(error_b.cmp(&error_a), Ordering::Greater);
361    }
362}