Created
April 26, 2021 21:05
-
-
Save rbtcollins/d6d7d9ac4a6f714c05bc421b428cae04 to your computer and use it in GitHub Desktop.
error-chain migration support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Inspired by failure::SyncFailure, but not identical. | |
/// | |
/// SyncError does not grant full safety: it will panic when errors are used | |
/// across threads (e.g. by threaded error logging libraries). This could be | |
/// fixed, but as we don't do that within rustup, it is not needed. If using | |
/// this code elsewhere, just hunt down and remove the unchecked unwraps. | |
pub struct SyncError<T: 'static> { | |
inner: Arc<Mutex<T>>, | |
proxy: Option<CauseProxy<T>>, | |
} | |
impl<T: std::error::Error + 'static> SyncError<T> { | |
pub fn new(err: T) -> Self { | |
let arc = Arc::new(Mutex::new(err)); | |
let proxy = match arc.lock().unwrap().source() { | |
None => None, | |
Some(source) => Some(CauseProxy::new(source, Arc::downgrade(&arc), 0)), | |
}; | |
SyncError { inner: arc, proxy } | |
} | |
pub fn maybe<R>(r: std::result::Result<R, T>) -> std::result::Result<R, Self> { | |
match r { | |
Ok(v) => Ok(v), | |
Err(e) => Err(SyncError::new(e)), | |
} | |
} | |
pub fn unwrap(self) -> T { | |
Arc::try_unwrap(self.inner).unwrap().into_inner().unwrap() | |
} | |
pub fn as_ref(&self) -> MutexGuard<'_, T> { | |
Arc::as_ref(&self.inner).lock().unwrap() | |
} | |
} | |
impl<T: std::error::Error + 'static> std::error::Error for SyncError<T> { | |
#[cfg(backtrace)] | |
fn backtrace(&self) -> Option<&Backtrace> {} | |
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | |
self.proxy.as_ref().map(|x| x as _) | |
} | |
} | |
impl<T> Display for SyncError<T> | |
where | |
T: Display, | |
{ | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
self.inner.lock().unwrap().fmt(f) | |
} | |
} | |
impl<T> Debug for SyncError<T> | |
where | |
T: Debug, | |
{ | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
self.inner.lock().unwrap().fmt(f) | |
} | |
} | |
struct CauseProxy<T: 'static> { | |
inner: Weak<Mutex<T>>, | |
next: Option<Box<CauseProxy<T>>>, | |
depth: u32, | |
} | |
impl<T: std::error::Error> CauseProxy<T> { | |
fn new(err: &dyn std::error::Error, weak: Weak<Mutex<T>>, depth: u32) -> Self { | |
// Can't allocate an object, or mutate the proxy safely during source(), | |
// so we just take the hit at construction, recursively. We can't hold | |
// references outside the mutex at all, so instead we remember how many | |
// steps to get to this proxy. And if some error chain plays tricks, the | |
// user gets both pieces. | |
CauseProxy { | |
inner: weak.clone(), | |
depth, | |
next: match err.source() { | |
None => None, | |
Some(source) => Some(Box::new(CauseProxy::new(source, weak, depth + 1))), | |
}, | |
} | |
} | |
fn with_instance<R, F>(&self, f: F) -> R | |
where | |
F: FnOnce(&(dyn std::error::Error + 'static)) -> R, | |
{ | |
let arc = self.inner.upgrade().unwrap(); | |
{ | |
let e = arc.lock().unwrap(); | |
let mut source = e.source().unwrap(); | |
for _ in 0..self.depth { | |
source = source.source().unwrap(); | |
} | |
f(source) | |
} | |
} | |
} | |
impl<T: std::error::Error + 'static> std::error::Error for CauseProxy<T> { | |
#[cfg(backtrace)] | |
fn backtrace(&self) -> Option<&Backtrace> {} | |
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | |
self.next.as_ref().map(|x| x as _) | |
} | |
} | |
impl<T> Display for CauseProxy<T> | |
where | |
T: Display + std::error::Error, | |
{ | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
self.with_instance(|i| std::fmt::Display::fmt(&i, f)) | |
} | |
} | |
impl<T> Debug for CauseProxy<T> | |
where | |
T: Debug + std::error::Error, | |
{ | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
self.with_instance(|i| std::fmt::Debug::fmt(&i, f)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment