rutie/class/traits/
exception.rs

1use crate::{binding::vm, util, AnyObject, Array, Class, Object, RString};
2
3/// Descendants of class Exception are used to communicate between Kernel#raise
4/// and rescue statements in `begin ... end` blocks. Exception objects carry
5/// information about the exception – its type (the exception's class name), an
6/// optional descriptive string, and optional traceback information. Exception
7/// subclasses may add additional information like NameError#name.
8///
9/// Programs may make subclasses of Exception, typically of StandardError or
10/// RuntimeError, to provide custom classes and add additional information.
11/// See the subclass list below for defaults for `raise` and `rescue`.
12pub trait Exception: Object {
13    /// Construct a new Exception object, optionally passing in a message.
14    ///
15    /// # Examples
16    /// ```
17    /// use rutie::{AnyException, Exception, Object, VM};
18    /// # VM::init();
19    ///
20    /// assert_eq!(
21    ///   AnyException::new("StandardError", None).to_s(),
22    ///   "StandardError"
23    /// );
24    /// ```
25    ///
26    /// A nested exception
27    ///
28    /// ```
29    /// use rutie::{AnyException, Exception, Object, VM, Class};
30    /// # VM::init();
31    ///
32    /// let mut klass = Class::new("MyGem", None);
33    /// let se = Class::from_existing("StandardError");
34    /// let _ = klass.define_nested_class("MyError", Some(&se));
35    ///
36    /// assert_eq!(
37    ///   AnyException::new("MyGem::MyError", None).to_s(),
38    ///   "MyGem::MyError"
39    /// );
40    /// ```
41    fn new(class: &str, msg: Option<&str>) -> Self {
42        let class = util::inmost_rb_object(class);
43        let msg = msg.map(|s| RString::new_utf8(s).value());
44
45        Self::from(vm::call_method(class, "new", util::option_to_slice(&msg)))
46    }
47
48    /// With no argument, or if the argument is the same as the receiver,
49    /// return the receiver. Otherwise, create a new exception object of
50    /// the same class as the receiver, but with a message equal
51    /// to `string.to_str`.
52    ///
53    /// # Examples
54    /// ```
55    /// use rutie::{AnyException, Exception, Object, VM};
56    /// # VM::init();
57    ///
58    /// assert_eq!(
59    ///   AnyException::new("StandardError", Some("something went wrong")).exception(None),
60    ///   AnyException::new("StandardError", Some("something went wrong"))
61    /// );
62    /// ```
63    fn exception(&self, string: Option<&str>) -> Self {
64        let string = string.map(|s| RString::new_utf8(s).value());
65
66        Self::from(vm::call_method(
67            self.value(),
68            "exception",
69            util::option_to_slice(&string),
70        ))
71    }
72
73    /// Returns any backtrace associated with the exception. The
74    /// backtrace is an array of strings, each containing either
75    /// “filename:lineNo: in `method''' or “filename:lineNo.''
76    ///
77    /// # Examples
78    /// ```
79    /// use rutie::{AnyException, Exception, Object, VM, RString};
80    /// # VM::init();
81    ///
82    /// let x = AnyException::new("StandardError", Some("something went wrong"));
83    ///
84    /// assert!(x.backtrace().is_none());
85    /// ```
86    fn backtrace(&self) -> Option<Array> {
87        let result = vm::call_method(self.value(), "backtrace", &[]);
88
89        if result.is_nil() {
90            return None;
91        }
92
93        Some(Array::from(result))
94    }
95
96    /// Returns any backtrace associated with the exception. This
97    /// method is similar to #backtrace, but the backtrace is an
98    /// array of Thread::Backtrace::Location.
99    ///
100    /// Now, this method is not affected by #set_backtrace.
101    ///
102    /// # Examples
103    /// ```
104    /// use rutie::{AnyException, Exception, Object, VM, RString};
105    /// # VM::init();
106    ///
107    /// let x = AnyException::new("StandardError", Some("something went wrong"));
108    ///
109    /// assert!(x.backtrace_locations().is_none());
110    /// ```
111    fn backtrace_locations(&self) -> Option<Array> {
112        let result = vm::call_method(self.value(), "backtrace_locations", &[]);
113
114        if result.is_nil() {
115            return None;
116        }
117
118        Some(Array::from(result))
119    }
120
121    /// Returns the previous exception at the time this
122    /// exception was raised. This is useful for wrapping exceptions
123    /// and retaining the original exception information.
124    ///
125    /// # Examples
126    /// ```
127    /// use rutie::{AnyException, Exception, Object, VM, RString};
128    /// # VM::init();
129    ///
130    /// let x = AnyException::new("StandardError", Some("something went wrong"));
131    ///
132    /// assert!(x.cause().is_none());
133    /// ```
134    fn cause(&self) -> Option<Self> {
135        let result = vm::call_method(self.value(), "cause", &[]);
136
137        if result.is_nil() {
138            return None;
139        }
140
141        Some(Self::from(result))
142    }
143
144    /// Return this exception's class name and message
145    ///
146    /// # Examples
147    /// ```
148    /// use rutie::{AnyException, Exception, Object, VM};
149    /// # VM::init();
150    ///
151    /// assert_eq!(
152    ///   AnyException::new("StandardError", Some("oops")).inspect(),
153    ///   "#<StandardError: oops>"
154    /// );
155    /// ```
156    fn inspect(&self) -> String {
157        RString::from(vm::call_method(self.value(), "inspect", &[])).to_string()
158    }
159
160    /// Returns the result of invoking `exception.to_s`. Normally this
161    /// returns the exception's message or name.
162    ///
163    /// # Examples
164    /// ```
165    /// use rutie::{AnyException, Exception, Object, VM};
166    /// # VM::init();
167    ///
168    /// assert_eq!(
169    ///   AnyException::new("StandardError", Some("oops")).message(),
170    ///   "oops"
171    /// );
172    /// ```
173    fn message(&self) -> String {
174        RString::from(vm::call_method(self.value(), "message", &[])).to_string()
175    }
176
177    /// Sets the backtrace information associated with exc. The backtrace
178    /// must be an array of String objects or a single String in the format
179    /// described in #backtrace.
180    ///
181    /// # Examples
182    /// ```
183    /// use rutie::{AnyException, Exception, Object, VM, RString, Array};
184    /// # VM::init();
185    ///
186    /// let x = AnyException::new("StandardError", Some("something went wrong"));
187    ///
188    /// let mut arr = Array::new();
189    /// arr.push(RString::new_utf8("prog.rb:10"));
190    ///
191    /// x.set_backtrace(arr.to_any_object());
192    ///
193    /// assert_eq!(
194    ///   x.backtrace().
195    ///     unwrap().
196    ///     pop().
197    ///     try_convert_to::<RString>().
198    ///     unwrap().
199    ///     to_string(),
200    ///   "prog.rb:10"
201    /// );
202    /// ```
203    fn set_backtrace(&self, backtrace: AnyObject) -> Option<Array> {
204        let result = vm::call_method(self.value(), "set_backtrace", &[backtrace.value()]);
205
206        if result.is_nil() {
207            return None;
208        }
209
210        Some(Array::from(result))
211    }
212
213    /// Returns exception's message (or the name of the exception if no message is set).
214    ///
215    /// # Examples
216    /// ```
217    /// use rutie::{AnyException, Exception, Object, VM};
218    /// # VM::init();
219    ///
220    /// assert_eq!(
221    ///   AnyException::new("StandardError", Some("oops")).to_s(),
222    ///   "oops"
223    /// );
224    /// ```
225    fn to_s(&self) -> String {
226        RString::from(vm::call_method(self.value(), "to_s", &[])).to_string()
227    }
228}