use std::fmt;
#[cfg(ruby_gte_3_1)]
use rb_sys::rb_eNoMatchingPatternKeyError;
use rb_sys::{
rb_eArgError, rb_eEOFError, rb_eEncCompatError, rb_eEncodingError, rb_eException, rb_eFatal,
rb_eFloatDomainError, rb_eFrozenError, rb_eIOError, rb_eIndexError, rb_eInterrupt,
rb_eKeyError, rb_eLoadError, rb_eLocalJumpError, rb_eMathDomainError, rb_eNameError,
rb_eNoMatchingPatternError, rb_eNoMemError, rb_eNoMethodError, rb_eNotImpError, rb_eRangeError,
rb_eRegexpError, rb_eRuntimeError, rb_eScriptError, rb_eSecurityError, rb_eSignal,
rb_eStandardError, rb_eStopIteration, rb_eSyntaxError, rb_eSysStackError, rb_eSystemCallError,
rb_eSystemExit, rb_eThreadError, rb_eTypeError, rb_eZeroDivError, VALUE,
};
use crate::{
class::{Class, RClass},
error::Error,
into_value::{ArgList, IntoValue},
module::Module,
object::Object,
r_array::RArray,
try_convert::TryConvert,
value::{
private::{self, ReprValue as _},
NonZeroValue, ReprValue, Value,
},
Ruby,
};
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Exception(NonZeroValue);
impl Exception {
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
debug_assert_value!(val);
unsafe {
val.class()
.is_inherited(RClass::from_rb_value_unchecked(rb_eException))
.then(|| Self(NonZeroValue::new_unchecked(val)))
}
}
#[inline]
pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
Self(NonZeroValue::new_unchecked(Value::new(val)))
}
pub fn exception_class(self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(self.class().as_rb_value()) }
}
}
impl fmt::Display for Exception {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe { self.to_s_infallible() })
}
}
impl fmt::Debug for Exception {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
unsafe {
writeln!(f, "{}: {}", self.classname(), self)?;
if let Ok(Some(backtrace)) = self.funcall::<_, _, Option<RArray>>("backtrace", ()) {
for line in backtrace {
writeln!(f, "{}", line)?;
}
}
}
Ok(())
} else {
write!(f, "{}", self.inspect())
}
}
}
impl IntoValue for Exception {
#[inline]
fn into_value_with(self, _: &Ruby) -> Value {
self.0.get()
}
}
unsafe impl private::ReprValue for Exception {}
impl ReprValue for Exception {}
impl TryConvert for Exception {
fn try_convert(val: Value) -> Result<Self, Error> {
if let Some(e) = Self::from_value(val) {
return Ok(e);
}
if let Some(Ok(val)) = val.check_funcall::<_, _, Value>("exception", ()) {
if let Some(e) = Self::from_value(val) {
return Ok(e);
}
}
Err(Error::new(
Ruby::get_with(val).exception_type_error(),
format!("no implicit conversion of {} into Exception", unsafe {
val.classname()
},),
))
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct ExceptionClass(NonZeroValue);
impl ExceptionClass {
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
debug_assert_value!(val);
unsafe {
RClass::from_value(val).and_then(|class| {
class
.is_inherited(RClass::from_rb_value_unchecked(rb_eException))
.then(|| Self(NonZeroValue::new_unchecked(val)))
})
}
}
#[inline]
pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
Self(NonZeroValue::new_unchecked(Value::new(val)))
}
}
impl fmt::Display for ExceptionClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe { self.to_s_infallible() })
}
}
impl fmt::Debug for ExceptionClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inspect())
}
}
impl IntoValue for ExceptionClass {
#[inline]
fn into_value_with(self, _: &Ruby) -> Value {
self.0.get()
}
}
impl Object for ExceptionClass {}
impl Module for ExceptionClass {}
impl Class for ExceptionClass {
type Instance = Exception;
fn new(superclass: Self) -> Result<Self, Error> {
RClass::new(superclass.as_r_class())
.map(|class| unsafe { ExceptionClass::from_value_unchecked(class.as_value()) })
}
fn new_instance<T>(self, args: T) -> Result<Self::Instance, Error>
where
T: ArgList,
{
self.as_r_class()
.new_instance(args)
.map(|ins| unsafe { Exception::from_value_unchecked(ins) })
}
fn obj_alloc(self) -> Result<Self::Instance, Error> {
self.as_r_class()
.obj_alloc()
.map(|ins| unsafe { Exception::from_value_unchecked(ins) })
}
fn as_r_class(self) -> RClass {
unsafe { RClass::from_value_unchecked(self.as_value()) }
}
}
unsafe impl private::ReprValue for ExceptionClass {}
impl ReprValue for ExceptionClass {}
impl TryConvert for ExceptionClass {
fn try_convert(val: Value) -> Result<Self, Error> {
Self::from_value(val).ok_or_else(|| {
Error::new(
Ruby::get_with(val).exception_type_error(),
format!(
"no implicit conversion of {} into Class inheriting Exception",
unsafe { val.classname() },
),
)
})
}
}
impl Ruby {
#[inline]
pub fn exception_arg_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eArgError) }
}
#[inline]
pub fn exception_eof_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eEOFError) }
}
#[inline]
pub fn exception_enc_compat_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eEncCompatError) }
}
#[inline]
pub fn exception_encoding_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eEncodingError) }
}
#[inline]
pub fn exception_exception(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eException) }
}
#[inline]
pub fn exception_fatal(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eFatal) }
}
#[inline]
pub fn exception_float_domain_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eFloatDomainError) }
}
#[inline]
pub fn exception_frozen_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eFrozenError) }
}
#[inline]
pub fn exception_io_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eIOError) }
}
#[inline]
pub fn exception_index_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eIndexError) }
}
#[inline]
pub fn exception_interrupt(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eInterrupt) }
}
#[inline]
pub fn exception_key_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eKeyError) }
}
#[inline]
pub fn exception_load_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eLoadError) }
}
#[inline]
pub fn exception_local_jump_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eLocalJumpError) }
}
#[inline]
pub fn exception_math_domain_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eMathDomainError) }
}
#[inline]
pub fn exception_name_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eNameError) }
}
#[inline]
pub fn exception_no_matching_pattern_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eNoMatchingPatternError) }
}
#[cfg(any(ruby_gte_3_1, docsrs))]
#[cfg_attr(docsrs, doc(cfg(ruby_gte_3_1)))]
#[inline]
pub fn exception_no_matching_pattern_key_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eNoMatchingPatternKeyError) }
}
#[inline]
pub fn exception_no_mem_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eNoMemError) }
}
#[inline]
pub fn exception_no_method_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eNoMethodError) }
}
#[inline]
pub fn exception_not_imp_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eNotImpError) }
}
#[inline]
pub fn exception_range_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eRangeError) }
}
#[inline]
pub fn exception_regexp_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eRegexpError) }
}
#[inline]
pub fn exception_runtime_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eRuntimeError) }
}
#[inline]
pub fn exception_script_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eScriptError) }
}
#[inline]
pub fn exception_security_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eSecurityError) }
}
#[inline]
pub fn exception_signal(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eSignal) }
}
#[inline]
pub fn exception_standard_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eStandardError) }
}
#[inline]
pub fn exception_stop_iteration(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eStopIteration) }
}
#[inline]
pub fn exception_syntax_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eSyntaxError) }
}
#[inline]
pub fn exception_sys_stack_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eSysStackError) }
}
#[inline]
pub fn exception_system_call_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eSystemCallError) }
}
#[inline]
pub fn exception_system_exit(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eSystemExit) }
}
#[inline]
pub fn exception_thread_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eThreadError) }
}
#[inline]
pub fn exception_type_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eTypeError) }
}
#[inline]
pub fn exception_zero_div_error(&self) -> ExceptionClass {
unsafe { ExceptionClass::from_rb_value_unchecked(rb_eZeroDivError) }
}
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_arg_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn arg_error() -> ExceptionClass {
get_ruby!().exception_arg_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_eof_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn eof_error() -> ExceptionClass {
get_ruby!().exception_eof_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_enc_compat_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn enc_compat_error() -> ExceptionClass {
get_ruby!().exception_enc_compat_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_encoding_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn encoding_error() -> ExceptionClass {
get_ruby!().exception_encoding_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_exception` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn exception() -> ExceptionClass {
get_ruby!().exception_exception()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_fatal` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn fatal() -> ExceptionClass {
get_ruby!().exception_fatal()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_float_domain_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn float_domain_error() -> ExceptionClass {
get_ruby!().exception_float_domain_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_frozen_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn frozen_error() -> ExceptionClass {
get_ruby!().exception_frozen_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_io_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn io_error() -> ExceptionClass {
get_ruby!().exception_io_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_index_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn index_error() -> ExceptionClass {
get_ruby!().exception_index_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_interrupt` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn interrupt() -> ExceptionClass {
get_ruby!().exception_interrupt()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_key_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn key_error() -> ExceptionClass {
get_ruby!().exception_key_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_load_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn load_error() -> ExceptionClass {
get_ruby!().exception_load_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_local_jump_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn local_jump_error() -> ExceptionClass {
get_ruby!().exception_local_jump_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_math_domain_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn math_domain_error() -> ExceptionClass {
get_ruby!().exception_math_domain_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_name_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn name_error() -> ExceptionClass {
get_ruby!().exception_name_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_no_matching_pattern_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn no_matching_pattern_error() -> ExceptionClass {
get_ruby!().exception_no_matching_pattern_error()
}
#[cfg(any(ruby_gte_3_1, docsrs))]
#[cfg_attr(docsrs, doc(cfg(ruby_gte_3_1)))]
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_no_matching_pattern_key_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn no_matching_pattern_key_error() -> ExceptionClass {
get_ruby!().exception_no_matching_pattern_key_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_no_mem_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn no_mem_error() -> ExceptionClass {
get_ruby!().exception_no_mem_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_no_method_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn no_method_error() -> ExceptionClass {
get_ruby!().exception_no_method_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_not_imp_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn not_imp_error() -> ExceptionClass {
get_ruby!().exception_not_imp_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_range_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn range_error() -> ExceptionClass {
get_ruby!().exception_range_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_regexp_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn regexp_error() -> ExceptionClass {
get_ruby!().exception_regexp_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_runtime_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn runtime_error() -> ExceptionClass {
get_ruby!().exception_runtime_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_script_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn script_error() -> ExceptionClass {
get_ruby!().exception_script_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_security_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn security_error() -> ExceptionClass {
get_ruby!().exception_security_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_signal` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn signal() -> ExceptionClass {
get_ruby!().exception_signal()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_standard_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn standard_error() -> ExceptionClass {
get_ruby!().exception_standard_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_stop_iteration` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn stop_iteration() -> ExceptionClass {
get_ruby!().exception_stop_iteration()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_syntax_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn syntax_error() -> ExceptionClass {
get_ruby!().exception_syntax_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_sys_stack_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn sys_stack_error() -> ExceptionClass {
get_ruby!().exception_sys_stack_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_system_call_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn system_call_error() -> ExceptionClass {
get_ruby!().exception_system_call_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_system_exit` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn system_exit() -> ExceptionClass {
get_ruby!().exception_system_exit()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_thread_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn thread_error() -> ExceptionClass {
get_ruby!().exception_thread_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_type_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn type_error() -> ExceptionClass {
get_ruby!().exception_type_error()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::exception_zero_div_error` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn zero_div_error() -> ExceptionClass {
get_ruby!().exception_zero_div_error()
}