spinoso_exception/
lib.rs

1#![warn(clippy::all, clippy::pedantic, clippy::undocumented_unsafe_blocks)]
2#![allow(
3    clippy::let_underscore_untyped,
4    reason = "https://github.com/rust-lang/rust-clippy/pull/10442#issuecomment-1516570154"
5)]
6#![allow(
7    clippy::question_mark,
8    reason = "https://github.com/rust-lang/rust-clippy/issues/8281"
9)]
10#![allow(clippy::manual_let_else, reason = "manual_let_else was very buggy on release")]
11#![allow(clippy::missing_errors_doc, reason = "A lot of existing code fails this lint")]
12#![allow(
13    clippy::unnecessary_lazy_evaluations,
14    reason = "https://github.com/rust-lang/rust-clippy/issues/8109"
15)]
16#![cfg_attr(
17    test,
18    allow(clippy::non_ascii_literal, reason = "tests sometimes require UTF-8 string content")
19)]
20#![allow(unknown_lints)]
21#![warn(
22    missing_copy_implementations,
23    missing_debug_implementations,
24    missing_docs,
25    rust_2024_compatibility,
26    trivial_casts,
27    trivial_numeric_casts,
28    unused_qualifications,
29    variant_size_differences
30)]
31#![forbid(unsafe_code)]
32// Enable feature callouts in generated documentation:
33// https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html
34//
35// This approach is borrowed from tokio.
36#![cfg_attr(docsrs, feature(doc_cfg))]
37#![cfg_attr(docsrs, feature(doc_alias))]
38
39//! Built in Ruby exception types.
40//!
41//! Descendants of class [`Exception`] are used to communicate between
42//! [`Kernel#raise`] and `rescue` statements in `begin ... end` blocks.
43//! Exception objects carry information about the exception – its type (the
44//! exception's class name), an optional descriptive string, and optional
45//! traceback information. `Exception` subclasses may add additional information
46//! like [`NameError#name`].
47//!
48//! # Ruby Exception Hierarchy
49//!
50//! The built-in subclasses of [`Exception`] are:
51//!
52//! - [`NoMemoryError`]
53//! - [`ScriptError`]
54//!   - [`LoadError`]
55//!   - [`NotImplementedError`]
56//!   - [`SyntaxError`]
57//! - [`SecurityError`]
58//! - [`SignalException`]
59//!   - [`Interrupt`]
60//! - [`StandardError`] — default for `rescue`
61//!   - [`ArgumentError`]
62//!     - [`UncaughtThrowError`]
63//!   - [`EncodingError`]
64//!   - [`FiberError`]
65//!   - [`IOError`]
66//!     - [`EOFError`]
67//!   - [`IndexError`]
68//!     - [`KeyError`]
69//!     - [`StopIteration`]
70//!   - [`LocalJumpError`]
71//!   - [`NameError`]
72//!     - [`NoMethodError`]
73//!   - [`RangeError`]
74//!     - [`FloatDomainError`]
75//!   - [`RegexpError`]
76//!   - [`RuntimeError`] — default for `raise`
77//!     - [`FrozenError`]
78//!   - [`SystemCallError`]
79//!     - `Errno::*`
80//!   - [`ThreadError`]
81//!   - [`TypeError`]
82//!   - [`ZeroDivisionError`]
83//! - [`SystemExit`]
84//! - [`SystemStackError`]
85//! - `fatal` — impossible to rescue
86//!
87//! # `no_std`
88//!
89//! This crate is `no_std` compatible when built without the `std` feature. This
90//! crate has a required dependency on [`alloc`].
91//!
92//! [`Exception`]: https://ruby-doc.org/core-3.1.2/Exception.html
93//! [`Kernel#raise`]: https://ruby-doc.org/core-3.1.2/Kernel.html#method-i-raise
94//! [`NameError#name`]: https://ruby-doc.org/core-3.1.2/NameError.html#method-i-name
95//! [`NoMemoryError`]: https://ruby-doc.org/core-3.1.2/NoMemoryError.html
96//! [`ScriptError`]: https://ruby-doc.org/core-3.1.2/ScriptError.html
97//! [`LoadError`]: https://ruby-doc.org/core-3.1.2/LoadError.html
98//! [`NotImplementedError`]: https://ruby-doc.org/core-3.1.2/NotImplementedError.html
99//! [`SyntaxError`]: https://ruby-doc.org/core-3.1.2/SyntaxError.html
100//! [`SecurityError`]: https://ruby-doc.org/core-3.1.2/SecurityError.html
101//! [`SignalException`]: https://ruby-doc.org/core-3.1.2/SignalException.html
102//! [`Interrupt`]: https://ruby-doc.org/core-3.1.2/Interrupt.html
103//! [`StandardError`]: https://ruby-doc.org/core-3.1.2/StandardError.html
104//! [`ArgumentError`]: https://ruby-doc.org/core-3.1.2/ArgumentError.html
105//! [`UncaughtThrowError`]: https://ruby-doc.org/core-3.1.2/UncaughtThrowError.html
106//! [`EncodingError`]: https://ruby-doc.org/core-3.1.2/EncodingError.html
107//! [`FiberError`]: https://ruby-doc.org/core-3.1.2/FiberError.html
108//! [`IOError`]: https://ruby-doc.org/core-3.1.2/IOError.html
109//! [`EOFError`]: https://ruby-doc.org/core-3.1.2/EOFError.html
110//! [`IndexError`]: https://ruby-doc.org/core-3.1.2/IndexError.html
111//! [`KeyError`]: https://ruby-doc.org/core-3.1.2/KeyError.html
112//! [`StopIteration`]: https://ruby-doc.org/core-3.1.2/StopIteration.html
113//! [`LocalJumpError`]: https://ruby-doc.org/core-3.1.2/LocalJumpError.html
114//! [`NameError`]: https://ruby-doc.org/core-3.1.2/NameError.html
115//! [`NoMethodError`]: https://ruby-doc.org/core-3.1.2/NoMethodError.html
116//! [`RangeError`]: https://ruby-doc.org/core-3.1.2/RangeError.html
117//! [`FloatDomainError`]: https://ruby-doc.org/core-3.1.2/FloatDomainError.html
118//! [`RegexpError`]: https://ruby-doc.org/core-3.1.2/RegexpError.html
119//! [`RuntimeError`]: https://ruby-doc.org/core-3.1.2/RuntimeError.html
120//! [`FrozenError`]: https://ruby-doc.org/core-3.1.2/FrozenError.html
121//! [`SystemCallError`]: https://ruby-doc.org/core-3.1.2/SystemCallError.html
122//! [`ThreadError`]: https://ruby-doc.org/core-3.1.2/ThreadError.html
123//! [`TypeError`]: https://ruby-doc.org/core-3.1.2/TypeError.html
124//! [`ZeroDivisionError`]: https://ruby-doc.org/core-3.1.2/ZeroDivisionError.html
125//! [`SystemExit`]: https://ruby-doc.org/core-3.1.2/SystemExit.html
126//! [`SystemStackError`]: https://ruby-doc.org/core-3.1.2/SystemStackError.html
127
128#![no_std]
129
130// Ensure code blocks in `README.md` compile
131#[cfg(doctest)]
132#[doc = include_str!("../README.md")]
133mod readme {}
134
135extern crate alloc;
136
137use alloc::borrow::Cow;
138
139pub mod core;
140
141#[doc(inline)]
142pub use self::core::*;
143
144/// Polymorphic exception type that corresponds to Ruby's `Exception`.
145///
146/// This trait unifies all concrete exception types defined in this crate and is
147/// [object safe]. This means `RubyException` can be used as a trait object to
148/// represent an error type of any set of exception subclasses.
149///
150/// All types that implement `RubyException` should be `raise`able in an
151/// Artichoke Ruby VM.
152///
153/// # Examples
154///
155/// ```
156/// # use spinoso_exception::*;
157/// # struct Array(()); impl Array { pub fn is_frozen(&self) -> bool { true } }
158/// fn array_concat(slf: Array, other: Array) -> Result<Array, Box<dyn RubyException>> {
159///     if slf.is_frozen() {
160///         return Err(Box::new(FrozenError::new()));
161///     }
162///     Err(Box::new(NotImplementedError::new()))
163/// }
164/// ```
165///
166/// [object safe]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#object-safety-is-required-for-trait-objects
167pub trait RubyException {
168    /// The exception's message.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// # use spinoso_exception::*;
174    /// fn exception_inspect(exc: &dyn RubyException) {
175    ///     let message = exc.message();
176    ///     let message = String::from_utf8_lossy(&message);
177    ///     println!("{} ({})", exc.name(), message);
178    /// }
179    /// ```
180    ///
181    /// # Implementation notes
182    ///
183    /// This method returns a byte slice since Ruby `String`s are best
184    /// represented as a [`Vec<u8>`].
185    ///
186    /// [`Vec<u8>`]: alloc::vec::Vec
187    fn message(&self) -> Cow<'_, [u8]>;
188
189    /// The exception's class name.
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// # use spinoso_exception::*;
195    /// fn exception_inspect(exc: &dyn RubyException) {
196    ///     let message = exc.message();
197    ///     let message = String::from_utf8_lossy(&message);
198    ///     println!("{} ({})", exc.name(), message);
199    /// }
200    /// ```
201    fn name(&self) -> Cow<'_, str>;
202}
203
204// Assert that `RubyException` is object-safe (i.e. supports dynamic dispatch).
205const _: Option<&dyn RubyException> = None;