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