torrust_index_located_error/
lib.rs1use std::error::Error;
33use std::panic::Location;
34use std::sync::Arc;
35
36pub struct Located<E>(pub E);
40
41#[derive(Debug)]
43pub struct LocatedError<'a, E>
44where
45 E: Error + ?Sized + Send + Sync,
46{
47 source: Arc<E>,
48 location: Box<Location<'a>>,
49}
50
51impl<'a, E> std::fmt::Display for LocatedError<'a, E>
52where
53 E: Error + ?Sized + Send + Sync,
54{
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 write!(f, "{}, {}", self.source, self.location)
57 }
58}
59
60impl<'a, E> Error for LocatedError<'a, E>
61where
62 E: Error + ?Sized + Send + Sync + 'static,
63{
64 fn source(&self) -> Option<&(dyn Error + 'static)> {
65 Some(&self.source)
66 }
67}
68
69impl<'a, E> Clone for LocatedError<'a, E>
70where
71 E: Error + ?Sized + Send + Sync,
72{
73 fn clone(&self) -> Self {
74 LocatedError {
75 source: self.source.clone(),
76 location: self.location.clone(),
77 }
78 }
79}
80
81#[allow(clippy::from_over_into)]
82impl<'a, E> Into<LocatedError<'a, E>> for Located<E>
83where
84 E: Error + Send + Sync,
85 Arc<E>: Clone,
86{
87 #[track_caller]
88 fn into(self) -> LocatedError<'a, E> {
89 let e = LocatedError {
90 source: Arc::new(self.0),
91 location: Box::new(*std::panic::Location::caller()),
92 };
93 tracing::debug!("{e}");
94 e
95 }
96}
97
98#[allow(clippy::from_over_into)]
99impl<'a> Into<LocatedError<'a, dyn std::error::Error + Send + Sync>> for Arc<dyn std::error::Error + Send + Sync> {
100 #[track_caller]
101 fn into(self) -> LocatedError<'a, dyn std::error::Error + Send + Sync> {
102 LocatedError {
103 source: self,
104 location: Box::new(*std::panic::Location::caller()),
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use std::panic::Location;
112
113 use super::LocatedError;
114 use crate::Located;
115
116 #[derive(thiserror::Error, Debug)]
117 enum TestError {
118 #[error("Test")]
119 Test,
120 }
121
122 #[track_caller]
123 fn get_caller_location() -> Location<'static> {
124 *Location::caller()
125 }
126
127 #[test]
128 fn error_should_include_location() {
129 let e = TestError::Test;
130
131 let b: LocatedError<'_, TestError> = Located(e).into();
132 let l = get_caller_location();
133
134 assert_eq!(b.location.file(), l.file());
135 }
136}