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}