Skip to main content

error_stack/
iter.rs

1//! Iterators over [`Frame`]s.
2
3use alloc::{vec, vec::Vec};
4#[cfg(nightly)]
5use core::marker::PhantomData;
6use core::{fmt, iter::FusedIterator, slice::Iter};
7
8use crate::Frame;
9
10/// Helper function, which is used in both [`Frames`] and the mutable frame traversals on
11/// [`Report`].
12///
13/// [`Report`]: crate::Report
14///
15/// To traverse the frames, the following algorithm is used:
16/// Given a list of iterators, take the last iterator, and use items from it until the iterator has
17/// been exhausted. If that is the case (it returned `None`), remove the iterator from the list,
18/// and continue with the next iterator until all iterators are exhausted.
19///
20/// # Example
21///
22/// ```text
23/// 1) Out: - Stack: [A, G]
24/// 2) Out: A Stack: [G] [B, C]
25/// 3) Out: B Stack: [G] [E] [C, D]
26/// 4) Out: C Stack: [G] [E] [D]
27/// 4) Out: D Stack: [G] [E]
28/// 5) Out: E Stack: [G] [F]
29/// 6) Out: F Stack: [G]
30/// 7) Out: G Stack: [H]
31/// 8) Out: H Stack: -
32/// ```
33pub(crate) fn next<I: Iterator<Item = T>, T>(iter: &mut Vec<I>) -> Option<T> {
34    let out;
35    loop {
36        let last = iter.last_mut()?;
37
38        if let Some(next) = last.next() {
39            out = next;
40            break;
41        }
42
43        // exhausted, therefore cannot be used anymore.
44        iter.pop();
45    }
46
47    Some(out)
48}
49
50/// Iterator over the [`Frame`] stack of a [`Report`].
51///
52/// This uses an implementation of the Pre-Order, NLR Depth-First Search algorithm to resolve the
53/// tree.
54///
55/// Use [`Report::frames()`] to create this iterator.
56///
57/// # Example
58///
59/// This shows in numbers the index of the different depths, using this it's possible to linearize
60/// all frames and sort topologically, meaning that this ensures no child ever is before its parent.
61///
62/// Iterating the following report will return the frames in alphabetical order:
63///
64/// ```text
65/// A
66/// ╰┬▶ B
67///  │  ╰┬▶ C
68///  │   ╰▶ D
69///  ╰▶ E
70///     ╰─▶ F
71/// G
72/// ╰─▶ H
73/// ```
74///
75/// [`Report`]: crate::Report
76/// [`Report::frames()`]: crate::Report::frames
77#[must_use]
78#[derive(Clone)]
79pub struct Frames<'r> {
80    stack: Vec<Iter<'r, Frame>>,
81}
82
83impl<'r> Frames<'r> {
84    pub(crate) fn new(frames: &'r [Frame]) -> Self {
85        Self {
86            stack: vec![frames.iter()],
87        }
88    }
89}
90
91impl<'r> Iterator for Frames<'r> {
92    type Item = &'r Frame;
93
94    fn next(&mut self) -> Option<Self::Item> {
95        let frame = next(&mut self.stack)?;
96
97        self.stack.push(frame.sources().iter());
98        Some(frame)
99    }
100}
101
102impl FusedIterator for Frames<'_> {}
103
104impl fmt::Debug for Frames<'_> {
105    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
106        fmt.debug_list().entries(self.clone()).finish()
107    }
108}
109
110/// Iterator over requested references in the [`Frame`] stack of a [`Report`].
111///
112/// Use [`Report::request_ref()`] to create this iterator.
113///
114/// [`Report`]: crate::Report
115/// [`Report::request_ref()`]: crate::Report::request_ref
116#[must_use]
117#[cfg(nightly)]
118pub struct RequestRef<'r, T: ?Sized> {
119    frames: Frames<'r>,
120    _marker: PhantomData<&'r T>,
121}
122
123#[cfg(nightly)]
124impl<'r, T: ?Sized> RequestRef<'r, T> {
125    pub(super) fn new(frames: &'r [Frame]) -> Self {
126        Self {
127            frames: Frames::new(frames),
128            _marker: PhantomData,
129        }
130    }
131}
132
133#[cfg(nightly)]
134impl<'r, T> Iterator for RequestRef<'r, T>
135where
136    T: ?Sized + 'static,
137{
138    type Item = &'r T;
139
140    fn next(&mut self) -> Option<Self::Item> {
141        self.frames.by_ref().find_map(Frame::request_ref)
142    }
143}
144
145#[cfg(nightly)]
146impl<T> FusedIterator for RequestRef<'_, T> where T: ?Sized + 'static {}
147
148#[cfg(nightly)]
149impl<T: ?Sized> Clone for RequestRef<'_, T> {
150    fn clone(&self) -> Self {
151        Self {
152            frames: self.frames.clone(),
153            _marker: PhantomData,
154        }
155    }
156}
157
158#[cfg(nightly)]
159impl<T> fmt::Debug for RequestRef<'_, T>
160where
161    T: ?Sized + fmt::Debug + 'static,
162{
163    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
164        fmt.debug_list().entries(self.clone()).finish()
165    }
166}
167
168/// Iterator over requested values in the [`Frame`] stack of a [`Report`].
169///
170/// Use [`Report::request_value()`] to create this iterator.
171///
172/// [`Report`]: crate::Report
173/// [`Report::request_value()`]: crate::Report::request_value
174#[must_use]
175#[cfg(nightly)]
176pub struct RequestValue<'r, T> {
177    frames: Frames<'r>,
178    _marker: PhantomData<T>,
179}
180
181#[cfg(nightly)]
182impl<'r, T> RequestValue<'r, T> {
183    pub(super) fn new(frames: &'r [Frame]) -> Self {
184        Self {
185            frames: Frames::new(frames),
186            _marker: PhantomData,
187        }
188    }
189}
190
191#[cfg(nightly)]
192impl<T> Iterator for RequestValue<'_, T>
193where
194    T: 'static,
195{
196    type Item = T;
197
198    fn next(&mut self) -> Option<Self::Item> {
199        self.frames.find_map(Frame::request_value)
200    }
201}
202
203#[cfg(nightly)]
204impl<T> FusedIterator for RequestValue<'_, T> where T: 'static {}
205
206#[cfg(nightly)]
207impl<T> Clone for RequestValue<'_, T> {
208    fn clone(&self) -> Self {
209        Self {
210            frames: self.frames.clone(),
211            _marker: PhantomData,
212        }
213    }
214}
215
216#[cfg(nightly)]
217impl<T> fmt::Debug for RequestValue<'_, T>
218where
219    T: fmt::Debug + 'static,
220{
221    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
222        fmt.debug_list().entries(self.clone()).finish()
223    }
224}