Skip to main content

module_util/evaluator/
trace.rs

1//! Evaluation module trace.
2
3use core::iter::FusedIterator;
4use core::slice;
5
6use alloc::vec::Vec;
7
8///////////////////////////////////////////////////////////////////////////////
9
10/// A stack-like structure to hold the module backtrace during evaluation.
11///
12/// A module trace is to the evaluator what [`Backtrace`] is to a program. In
13/// the trace, modules are stored in import-order. This means that the module
14/// where the error was caused is always the last one in the trace.
15///
16/// [`Backtrace`]: std::backtrace::Backtrace
17#[derive(Debug, Clone)]
18pub struct Trace<T>(Vec<T>);
19
20impl<T> Default for Trace<T> {
21    fn default() -> Self {
22        Self::empty()
23    }
24}
25
26impl<T> From<Vec<T>> for Trace<T> {
27    fn from(x: Vec<T>) -> Self {
28        Self(x)
29    }
30}
31
32impl<T> From<Trace<T>> for Vec<T> {
33    fn from(x: Trace<T>) -> Self {
34        x.0
35    }
36}
37
38impl<T> Trace<T> {
39    /// Create a new empty [`Trace`].
40    #[must_use]
41    pub const fn empty() -> Self {
42        Self(Vec::new())
43    }
44
45    /// Get the number of modules in the trace.
46    #[must_use]
47    pub fn len(&self) -> usize {
48        self.0.len()
49    }
50
51    /// Check whether the trace is empty.
52    #[must_use]
53    pub fn is_empty(&self) -> bool {
54        self.0.is_empty()
55    }
56
57    /// Get a reference to the current module.
58    ///
59    /// The current module is always the one that was [`push`]ed last. Returns
60    /// [`None`] if the trace is empty.
61    ///
62    /// [`push`]: Trace::push
63    #[must_use]
64    pub fn current(&self) -> Option<&T> {
65        self.0.last()
66    }
67
68    /// Add a module to the trace.
69    ///
70    /// Pushing a module makes that module the "deepest" level of the trace.
71    /// See: [`Trace`].
72    ///
73    /// # Example
74    ///
75    /// ```rust
76    /// # use module_util::evaluator::trace::Trace;
77    /// let mut trace = Trace::empty();
78    /// trace.push("module 1");
79    /// trace.push("module 2");
80    /// trace.push("module 3");
81    ///
82    /// assert_eq!(
83    ///     trace.iter().copied().collect::<Vec<_>>(),
84    ///     &[
85    ///         "module 1",
86    ///         "module 2",
87    ///         "module 3"
88    ///     ]
89    /// );
90    /// ```
91    pub fn push(&mut self, id: T) {
92        self.0.push(id);
93    }
94
95    /// Remove the last module from the trace and return it.
96    ///
97    /// Also see: [`push`].
98    ///
99    /// # Example
100    /// ```rust
101    /// # use module_util::evaluator::trace::Trace;
102    /// let mut trace = Trace::empty();
103    /// trace.push("module 1");
104    /// trace.push("module 2");
105    /// trace.push("module 3");
106    ///
107    /// assert_eq!(trace.pop(), Some("module 3"));
108    /// assert_eq!(trace.pop(), Some("module 2"));
109    /// assert_eq!(trace.pop(), Some("module 1"));
110    /// assert_eq!(trace.pop(), None);
111    /// ```
112    ///
113    /// [`push`]: Trace::push
114    pub fn pop(&mut self) -> Option<T> {
115        self.0.pop()
116    }
117
118    /// Get an iterator over all modules in the trace.
119    ///
120    /// The returned iterator traverses the trace from the deepest module to the
121    /// shallowest. The returned iterator implements [`DoubleEndedIterator`] so
122    /// you can use [`Iterator::rev`].
123    ///
124    /// # Example
125    ///
126    /// ```rust
127    /// # use module_util::evaluator::trace::Trace;
128    /// let mut trace = Trace::empty();
129    /// trace.push("module 1");
130    /// trace.push("module 2");
131    /// trace.push("module 3");
132    ///
133    /// let mut iter = trace.iter();
134    /// assert_eq!(iter.next().copied(), Some("module 1"));
135    /// assert_eq!(iter.next().copied(), Some("module 2"));
136    /// assert_eq!(iter.next().copied(), Some("module 3"));
137    /// assert_eq!(iter.next().copied(), None);
138    /// ```
139    #[must_use]
140    pub fn iter(&self) -> Iter<'_, T> {
141        Iter(self.0.iter())
142    }
143}
144
145impl<'a, T> IntoIterator for &'a Trace<T> {
146    type Item = &'a T;
147    type IntoIter = Iter<'a, T>;
148
149    fn into_iter(self) -> Self::IntoIter {
150        self.iter()
151    }
152}
153
154///////////////////////////////////////////////////////////////////////////////
155
156/// Borrowing iterator over [`Trace`].
157///
158/// See: [`Trace::iter`].
159#[derive(Debug, Clone)]
160pub struct Iter<'a, T>(slice::Iter<'a, T>);
161
162impl<'a, T> Iterator for Iter<'a, T> {
163    type Item = &'a T;
164
165    fn next(&mut self) -> Option<Self::Item> {
166        self.0.next()
167    }
168
169    fn size_hint(&self) -> (usize, Option<usize>) {
170        self.0.size_hint()
171    }
172}
173
174impl<T> DoubleEndedIterator for Iter<'_, T> {
175    fn next_back(&mut self) -> Option<Self::Item> {
176        self.0.next_back()
177    }
178}
179
180impl<T> ExactSizeIterator for Iter<'_, T> {
181    fn len(&self) -> usize {
182        self.0.len()
183    }
184}
185
186impl<T> FusedIterator for Iter<'_, T> {}