use std::backtrace::Backtrace;
use std::fmt;
use std::sync::Arc;
#[cfg(feature = "multi_thread")]
use std::io;
#[derive(Clone, Debug)]
pub(crate) enum ErrorEnum {
CallbackAlreadyInstalled,
#[cfg(feature = "weak_pointer")]
NotYetInitialized(Option<Arc<Backtrace>>),
#[cfg(feature = "weak_pointer")]
Expired(Option<Arc<Backtrace>>),
#[cfg(feature = "weak_pointer")]
UnvaluedWeak,
#[cfg(feature = "multi_thread")]
InternalLockingError,
OriginExpired(Option<Arc<Backtrace>>),
#[cfg(feature = "multi_thread")]
ThreadSpawnFailed(Arc<io::Error>),
}
impl Eq for ErrorEnum {}
impl PartialEq for ErrorEnum {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ErrorEnum::CallbackAlreadyInstalled, ErrorEnum::CallbackAlreadyInstalled) => true,
#[cfg(feature = "weak_pointer")]
(ErrorEnum::NotYetInitialized(_), ErrorEnum::NotYetInitialized(_)) => true,
#[cfg(feature = "weak_pointer")]
(ErrorEnum::Expired(_), ErrorEnum::Expired(_)) => true,
#[cfg(feature = "weak_pointer")]
(ErrorEnum::UnvaluedWeak, ErrorEnum::UnvaluedWeak) => true,
#[cfg(feature = "multi_thread")]
(ErrorEnum::InternalLockingError, ErrorEnum::InternalLockingError) => true,
(ErrorEnum::OriginExpired(_), ErrorEnum::OriginExpired(_)) => true,
#[cfg(feature = "multi_thread")]
(ErrorEnum::ThreadSpawnFailed(_), ErrorEnum::ThreadSpawnFailed(_)) => true,
_ => false,
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Error {
code: ErrorEnum,
}
impl Error {
#[inline]
pub(crate) const fn new(code: ErrorEnum) -> Error {
Error { code }
}
#[inline]
pub fn object_creation_backtrace(&self) -> Option<&Backtrace> {
match &self.code {
ErrorEnum::CallbackAlreadyInstalled => None,
#[cfg(feature = "weak_pointer")]
ErrorEnum::NotYetInitialized(opt_backtrace) => {
opt_backtrace.as_ref().map(|backtrace| &**backtrace)
}
#[cfg(feature = "weak_pointer")]
ErrorEnum::Expired(opt_backtrace) => {
opt_backtrace.as_ref().map(|backtrace| &**backtrace)
}
#[cfg(feature = "weak_pointer")]
ErrorEnum::UnvaluedWeak => None,
#[cfg(feature = "multi_thread")]
ErrorEnum::InternalLockingError => None,
ErrorEnum::OriginExpired(opt_backtrace) => {
opt_backtrace.as_ref().map(|backtrace| &**backtrace)
}
#[cfg(feature = "multi_thread")]
ErrorEnum::ThreadSpawnFailed(_) => None,
}
}
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
#[allow(
clippy::missing_inline_in_public_items,
reason = "Inlining this function won't generate much benefit."
)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
enum MaybeBacktrace<'a> {
Never,
None,
Some(&'a Backtrace),
}
impl<'a> MaybeBacktrace<'a> {
fn new(opt_backtrace: &'a Option<Arc<Backtrace>>) -> Self {
opt_backtrace
.as_ref()
.map_or(MaybeBacktrace::None, |backtrace| {
MaybeBacktrace::Some(backtrace)
})
}
}
let (msg, opt_backtrace): (&'static str, MaybeBacktrace) = match &self.code {
ErrorEnum::CallbackAlreadyInstalled => {
("callback already installed", MaybeBacktrace::Never)
}
#[cfg(feature = "weak_pointer")]
ErrorEnum::NotYetInitialized(opt_backtrace) => (
"weak pointer points at not-yet-initialized object",
MaybeBacktrace::new(opt_backtrace),
),
#[cfg(feature = "weak_pointer")]
ErrorEnum::Expired(opt_backtrace) => {
("object has expired", MaybeBacktrace::new(opt_backtrace))
}
#[cfg(feature = "weak_pointer")]
ErrorEnum::UnvaluedWeak => ("weak pointer never had a value", MaybeBacktrace::Never),
#[cfg(feature = "multi_thread")]
ErrorEnum::InternalLockingError => ("internal lock is poisoned", MaybeBacktrace::Never),
ErrorEnum::OriginExpired(opt_backtrace) => (
"unable to dereference/clone member-pointer, because its origin has expired",
MaybeBacktrace::new(opt_backtrace),
),
#[cfg(feature = "multi_thread")]
ErrorEnum::ThreadSpawnFailed(io_err) => {
return write!(f, "unable to start GC thread: {}", io_err);
}
};
match opt_backtrace {
MaybeBacktrace::Never => write!(f, "{}", msg),
MaybeBacktrace::None => write!(
f,
"{}\nuse RUST_BACKTRACE=1 to get a backtrace of when the pointer was constructed",
msg,
),
MaybeBacktrace::Some(backtrace) => {
write!(f, "{}\nobject constructed at:\n{}", msg, backtrace)
}
}
}
}
impl fmt::Debug for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&self.code, f)
}
}
#[cfg(test)]
mod tests {
use super::{Error, ErrorEnum};
fn is_send<T: Send>(_: &T) -> bool {
true
}
fn is_sync<T: Sync>(_: &T) -> bool {
true
}
#[test]
fn error_is_send_and_sync() {
let e = Error::new(ErrorEnum::CallbackAlreadyInstalled);
assert!(is_send(&e));
assert!(is_sync(&e));
}
}