use crate::{binding::vm, util, AnyObject, Array, Class, Object, RString};
pub trait Exception: Object {
fn new(class: &str, msg: Option<&str>) -> Self {
let class = util::inmost_rb_object(class);
let msg = msg.map(|s| RString::new_utf8(s).value());
Self::from(vm::call_method(class, "new", util::option_to_slice(&msg)))
}
fn exception(&self, string: Option<&str>) -> Self {
let string = string.map(|s| RString::new_utf8(s).value());
Self::from(vm::call_method(
self.value(),
"exception",
util::option_to_slice(&string),
))
}
fn backtrace(&self) -> Option<Array> {
let result = vm::call_method(self.value(), "backtrace", &[]);
if result.is_nil() {
return None;
}
Some(Array::from(result))
}
fn backtrace_locations(&self) -> Option<Array> {
let result = vm::call_method(self.value(), "backtrace_locations", &[]);
if result.is_nil() {
return None;
}
Some(Array::from(result))
}
fn cause(&self) -> Option<Self> {
let result = vm::call_method(self.value(), "cause", &[]);
if result.is_nil() {
return None;
}
Some(Self::from(result))
}
fn inspect(&self) -> String {
RString::from(vm::call_method(self.value(), "inspect", &[])).to_string()
}
fn message(&self) -> String {
RString::from(vm::call_method(self.value(), "message", &[])).to_string()
}
fn set_backtrace(&self, backtrace: AnyObject) -> Option<Array> {
let result = vm::call_method(self.value(), "set_backtrace", &[backtrace.value()]);
if result.is_nil() {
return None;
}
Some(Array::from(result))
}
fn to_s(&self) -> String {
RString::from(vm::call_method(self.value(), "to_s", &[])).to_string()
}
}
#[cfg(test)]
mod tests {
use crate::{AnyException, AnyObject, Array, Class, Exception, Object, RString, VM};
#[test]
fn test_exception_trait() {
crate::on_ruby_thread(|| {
let error = AnyException::new("ArgumentError", Some("bad value"));
assert_eq!(error.message(), "bad value");
assert_eq!(error.to_s(), "bad value");
assert_eq!(error.inspect(), "#<ArgumentError: bad value>");
assert!(Class::argument_error().case_equals(&error));
let copy = error.exception(Some("other"));
assert_eq!(copy.message(), "other");
assert_eq!(error.message(), "bad value");
assert!(error.exception(None).equals(&error));
assert!(error.backtrace().is_none());
assert!(error.backtrace_locations().is_none());
let mut trace = Array::new();
trace.push(RString::new_utf8("file.rb:1"));
let set = error.set_backtrace(trace.to_any_object()).unwrap();
assert_eq!(set.length(), 1);
assert_eq!(
error
.backtrace()
.unwrap()
.at(0)
.try_convert_to::<RString>()
.unwrap()
.to_str(),
"file.rb:1"
);
let raised = VM::eval(
"begin
begin
raise 'inner'
rescue
raise ArgumentError, 'outer'
end
rescue => e
e
end",
)
.unwrap()
.try_convert_to::<AnyException>()
.unwrap();
assert_eq!(raised.cause().unwrap().message(), "inner");
assert!(raised.cause().unwrap().cause().is_none());
assert!(raised.backtrace().unwrap().length() > 0);
assert!(raised.backtrace_locations().unwrap().length() > 0);
let any: AnyObject = raised.to_any_object();
assert!(any.try_convert_to::<AnyException>().is_ok());
});
}
}