1use core::error::Error as StdError;
4
5#[derive(Clone, Debug)]
17pub struct Chain<'a> {
18 next: Option<&'a (dyn StdError + 'static)>,
19}
20
21impl<'a> Chain<'a> {
22 #[inline]
23 fn new(head: &'a (dyn StdError + 'static)) -> Self {
24 Self { next: Some(head) }
25 }
26}
27
28impl<'a> Iterator for Chain<'a> {
29 type Item = &'a (dyn StdError + 'static);
30
31 #[inline]
32 fn next(&mut self) -> Option<Self::Item> {
33 let current = self.next?;
34 self.next = current.source();
35 Some(current)
36 }
37}
38
39impl core::iter::FusedIterator for Chain<'_> {}
40
41pub trait ErrorChainExt {
72 fn chain(&self) -> Chain<'_>;
80
81 fn root_cause(&self) -> &(dyn StdError + 'static);
84}
85
86#[inline]
90fn root_cause_of<'a>(head: &'a (dyn StdError + 'static)) -> &'a (dyn StdError + 'static) {
91 let mut cause = head;
92 while let Some(source) = cause.source() {
93 cause = source;
94 }
95 cause
96}
97
98impl<E: StdError + 'static> ErrorChainExt for E {
99 #[inline]
100 fn chain(&self) -> Chain<'_> {
101 Chain::new(self)
102 }
103
104 #[inline]
105 fn root_cause(&self) -> &(dyn StdError + 'static) {
106 root_cause_of(self)
107 }
108}
109
110impl ErrorChainExt for dyn StdError + 'static {
111 #[inline]
112 fn chain(&self) -> Chain<'_> {
113 Chain::new(self)
114 }
115
116 #[inline]
117 fn root_cause(&self) -> &(dyn StdError + 'static) {
118 root_cause_of(self)
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use std::fmt;
126
127 #[derive(Debug)]
128 struct Leaf;
129
130 impl fmt::Display for Leaf {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 f.write_str("leaf")
133 }
134 }
135
136 impl StdError for Leaf {}
137
138 #[derive(Debug)]
139 struct Layer {
140 label: &'static str,
141 source: Box<dyn StdError + 'static>,
142 }
143
144 impl fmt::Display for Layer {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 f.write_str(self.label)
147 }
148 }
149
150 impl StdError for Layer {
151 fn source(&self) -> Option<&(dyn StdError + 'static)> {
152 Some(&*self.source)
153 }
154 }
155
156 fn three_deep() -> Layer {
157 Layer {
158 label: "top",
159 source: Box::new(Layer {
160 label: "middle",
161 source: Box::new(Leaf),
162 }),
163 }
164 }
165
166 #[test]
167 fn chain_starts_at_self_then_walks_sources() {
168 let err = three_deep();
169 let labels: Vec<String> = err.chain().map(ToString::to_string).collect();
170 assert_eq!(labels, ["top", "middle", "leaf"]);
171 }
172
173 #[test]
174 fn chain_on_leaf_yields_only_self() {
175 let leaf = Leaf;
176 assert_eq!(leaf.chain().count(), 1);
177 assert_eq!(leaf.chain().next().unwrap().to_string(), "leaf");
178 }
179
180 #[test]
181 fn chain_over_dyn_error_from_source_starts_at_that_source() {
182 let err = three_deep();
183 let source = err.source().expect("has a source");
184 let labels: Vec<String> = source.chain().map(ToString::to_string).collect();
185 assert_eq!(labels, ["middle", "leaf"]);
186 }
187
188 #[test]
189 fn root_cause_is_the_deepest_link() {
190 let err = three_deep();
191 let root = err.root_cause();
192 assert_eq!(root.to_string(), "leaf");
193 let last = err.chain().last().expect("non-empty chain");
195 assert!(std::ptr::eq(
196 std::ptr::from_ref(root).cast::<()>(),
197 std::ptr::from_ref(last).cast::<()>(),
198 ));
199 }
200
201 #[test]
202 fn root_cause_of_leaf_is_itself() {
203 let leaf = Leaf;
204 assert_eq!(leaf.root_cause().to_string(), "leaf");
205 }
206}