module_util/
evaluator.rs

1//! Generic module [`Evaluator`].
2
3use core::fmt::{self, Display};
4use core::iter::FusedIterator;
5
6use alloc::collections::linked_list::{self, LinkedList};
7use alloc::vec::Vec;
8
9use module::Merge;
10use module::merge::error::Trace;
11
12use crate::Result;
13
14///////////////////////////////////////////////////////////////////////////////
15
16trait LinkedListExt<T> {
17    fn append_front(&mut self, other: &mut Self);
18}
19
20impl<T> LinkedListExt<T> for LinkedList<T> {
21    fn append_front(&mut self, other: &mut Self) {
22        other.append(self);
23        self.append(other);
24    }
25}
26
27///////////////////////////////////////////////////////////////////////////////
28
29#[derive(Debug, Clone)]
30enum Instruction<T> {
31    Enter(T),
32    Exit,
33    Eval(T),
34}
35
36impl<T> Instruction<T> {
37    fn as_eval(&self) -> Option<&T> {
38        if let Self::Eval(v) = self {
39            Some(v)
40        } else {
41            None
42        }
43    }
44
45    fn as_eval_mut(&mut self) -> Option<&mut T> {
46        if let Self::Eval(v) = self {
47            Some(v)
48        } else {
49            None
50        }
51    }
52
53    fn into_eval(self) -> Option<T> {
54        if let Self::Eval(v) = self {
55            Some(v)
56        } else {
57            None
58        }
59    }
60}
61
62///////////////////////////////////////////////////////////////////////////////
63
64/// A list of imports.
65///
66/// # serde
67///
68/// This type deserializes as a "sequence".
69///
70/// See: [serde data model](https://serde.rs/data-model.html).
71#[derive(Clone)]
72pub struct Imports<T>(LinkedList<Instruction<T>>);
73
74impl<T> fmt::Debug for Imports<T>
75where
76    T: fmt::Debug,
77{
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        f.debug_list()
80            .entries(self.0.iter().map(|x| {
81                x.as_eval()
82                    .expect("Imports should only have Eval instructions")
83            }))
84            .finish()
85    }
86}
87
88impl<T> FromIterator<T> for Imports<T> {
89    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
90        let inner = iter.into_iter().map(Instruction::Eval).collect();
91        Self(inner)
92    }
93}
94
95impl<T> From<T> for Imports<T> {
96    fn from(value: T) -> Self {
97        [value].into_iter().collect()
98    }
99}
100
101impl<T> Default for Imports<T> {
102    fn default() -> Self {
103        Self::empty()
104    }
105}
106
107impl<T> Imports<T> {
108    /// Create an empty import list.
109    pub const fn empty() -> Self {
110        Self(LinkedList::new())
111    }
112
113    /// Get the number of imports in this list.
114    pub fn len(&self) -> usize {
115        self.0.len()
116    }
117
118    /// Check whether the import list is empty.
119    pub fn is_empty(&self) -> bool {
120        self.0.is_empty()
121    }
122
123    /// Add `import` to the list.
124    pub fn push(&mut self, import: T) {
125        self.0.push_back(Instruction::Eval(import));
126    }
127
128    /// Move over all imports from `imports` to `self`.
129    ///
130    /// This function is equivalent to [`push`]ing each item of `imports` to
131    /// `self`.
132    ///
133    /// [`push`]: Imports::push
134    pub fn append(&mut self, imports: &mut Self) {
135        self.0.append(&mut imports.0);
136    }
137
138    /// Create an iterator over the imports of `self`.
139    pub fn iter(&self) -> Iter<'_, T> {
140        Iter(self.0.iter())
141    }
142
143    /// Create a mutable iterator over the imports of `self`.
144    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
145        IterMut(self.0.iter_mut())
146    }
147}
148
149impl<'a, T> IntoIterator for &'a Imports<T> {
150    type Item = &'a T;
151    type IntoIter = Iter<'a, T>;
152
153    fn into_iter(self) -> Self::IntoIter {
154        self.iter()
155    }
156}
157
158impl<'a, T> IntoIterator for &'a mut Imports<T> {
159    type Item = &'a mut T;
160    type IntoIter = IterMut<'a, T>;
161
162    fn into_iter(self) -> Self::IntoIter {
163        self.iter_mut()
164    }
165}
166
167impl<T> IntoIterator for Imports<T> {
168    type Item = T;
169    type IntoIter = IntoIter<T>;
170
171    fn into_iter(self) -> Self::IntoIter {
172        IntoIter(self.0.into_iter())
173    }
174}
175
176/// An iterator over [`Imports`].
177///
178/// Created by [`Imports::iter`].
179#[expect(missing_debug_implementations)]
180pub struct Iter<'a, T>(linked_list::Iter<'a, Instruction<T>>);
181
182impl<'a, T> Iterator for Iter<'a, T> {
183    type Item = &'a T;
184
185    fn next(&mut self) -> Option<Self::Item> {
186        self.0.next().map(|x| {
187            x.as_eval()
188                .expect("Imports should only have Eval instructions")
189        })
190    }
191
192    fn size_hint(&self) -> (usize, Option<usize>) {
193        self.0.size_hint()
194    }
195}
196
197impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
198    fn next_back(&mut self) -> Option<Self::Item> {
199        self.0.next_back().map(|x| {
200            x.as_eval()
201                .expect("Imports should only have Eval instructions")
202        })
203    }
204}
205
206impl<'a, T> ExactSizeIterator for Iter<'a, T> {
207    fn len(&self) -> usize {
208        self.0.len()
209    }
210}
211
212impl<'a, T> FusedIterator for Iter<'a, T> {}
213
214/// A mutable iterator over [`Imports`].
215///
216/// Created by [`Imports::iter_mut`].
217#[expect(missing_debug_implementations)]
218pub struct IterMut<'a, T>(linked_list::IterMut<'a, Instruction<T>>);
219
220impl<'a, T> Iterator for IterMut<'a, T> {
221    type Item = &'a mut T;
222
223    fn next(&mut self) -> Option<Self::Item> {
224        self.0.next().map(|x| {
225            x.as_eval_mut()
226                .expect("Imports should only have Eval instructions")
227        })
228    }
229
230    fn size_hint(&self) -> (usize, Option<usize>) {
231        self.0.size_hint()
232    }
233}
234
235impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
236    fn next_back(&mut self) -> Option<Self::Item> {
237        self.0.next_back().map(|x| {
238            x.as_eval_mut()
239                .expect("Imports should only have Eval instructions")
240        })
241    }
242}
243
244impl<'a, T> ExactSizeIterator for IterMut<'a, T> {
245    fn len(&self) -> usize {
246        self.0.len()
247    }
248}
249
250impl<'a, T> FusedIterator for IterMut<'a, T> {}
251
252/// Owning iterator over [`Imports`].
253///
254/// Created by [`Imports::into_iter`].
255#[expect(missing_debug_implementations)]
256pub struct IntoIter<T>(linked_list::IntoIter<Instruction<T>>);
257
258impl<T> Iterator for IntoIter<T> {
259    type Item = T;
260
261    fn next(&mut self) -> Option<Self::Item> {
262        self.0.next().map(|x| {
263            x.into_eval()
264                .expect("Imports should only have Eval instructions")
265        })
266    }
267
268    fn size_hint(&self) -> (usize, Option<usize>) {
269        self.0.size_hint()
270    }
271}
272
273impl<T> DoubleEndedIterator for IntoIter<T> {
274    fn next_back(&mut self) -> Option<Self::Item> {
275        self.0.next_back().map(|x| {
276            x.into_eval()
277                .expect("Imports should only have Eval instructions")
278        })
279    }
280}
281
282impl<T> ExactSizeIterator for IntoIter<T> {
283    fn len(&self) -> usize {
284        self.0.len()
285    }
286}
287
288impl<T> FusedIterator for IntoIter<T> {}
289
290#[cfg(feature = "serde")]
291mod serde_impl {
292    use super::*;
293
294    use core::marker::PhantomData;
295
296    use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
297
298    impl<'de, T> Deserialize<'de> for Imports<T>
299    where
300        T: Deserialize<'de>,
301    {
302        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
303        where
304            D: Deserializer<'de>,
305        {
306            struct ImportsVisitor<T>(PhantomData<T>);
307
308            impl<'de, T> Visitor<'de> for ImportsVisitor<T>
309            where
310                T: Deserialize<'de>,
311            {
312                type Value = Imports<T>;
313
314                fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
315                    formatter.write_str("a sequence")
316                }
317
318                fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
319                where
320                    A: SeqAccess<'de>,
321                {
322                    let mut inner = LinkedList::new();
323
324                    while let Some(e) = seq.next_element()? {
325                        inner.push_back(Instruction::Eval(e));
326                    }
327
328                    Ok(Imports(inner))
329                }
330            }
331
332            let visitor = ImportsVisitor(PhantomData);
333            deserializer.deserialize_seq(visitor)
334        }
335    }
336}
337
338///////////////////////////////////////////////////////////////////////////////
339
340/// A generic evaluator that evaluates modules of type `Value`.
341///
342/// This evaluator can be used to decouple the way modules are read from the way
343/// they are evaluated. It follows design paradigms of the ["sans-io" pattern].
344///
345/// This evaluator does not do anything on its own, it needs external code to
346/// "drive" it. Something like an "event loop".
347///
348/// ```rust,no_run
349/// # use module_util::evaluator::Evaluator;
350/// let mut evaluator = Evaluator::new();
351///
352/// evaluator.import("module 1");
353/// while let Some(this) = evaluator.next() {
354///     println!("evaluating {this}...");
355///
356///     // Read the value of the module here from the filesystem, network,
357///     // environment, etc.
358///     let value: i32 = unimplemented!();
359///
360///     // Resolve what other modules this module wants to import. This is also
361///     // specific to how you read a module. For example, it could be the
362///     // top-level "imports" array of the JSON file.
363///     let imports = unimplemented!();
364///
365///     evaluator.eval(this, imports, value).unwrap();
366/// }
367///
368/// let value = evaluator.finish().unwrap();
369/// println!("final value: {value}");
370/// ```
371///
372/// This pattern allows you to implement whatever kind of module logic you want
373/// inside the loop without modifying the evaluator.
374///
375/// ["sans-io" pattern]: https://sans-io.readthedocs.io/how-to-sans-io.html#how-to-write-i-o-free-protocol-implementations
376#[derive(Debug, Clone)]
377pub struct Evaluator<Ref, Value> {
378    program: LinkedList<Instruction<Ref>>,
379    trace: Vec<Ref>,
380    value: Option<Value>,
381}
382
383impl<Ref, Value> Default for Evaluator<Ref, Value> {
384    fn default() -> Self {
385        Self::new()
386    }
387}
388
389impl<Ref, Value> Evaluator<Ref, Value> {
390    /// Create a new evaluator.
391    pub const fn new() -> Self {
392        Self {
393            program: LinkedList::new(),
394            trace: Vec::new(),
395            value: None,
396        }
397    }
398
399    /// Create a new evaluator with an initial value.
400    ///
401    /// Calling [`finish`] on the returned [`Evaluator`] will never return
402    /// [`None`].
403    ///
404    /// [`finish`]: Evaluator::finish
405    pub const fn with(value: Value) -> Self {
406        Self {
407            program: LinkedList::new(),
408            trace: Vec::new(),
409            value: Some(value),
410        }
411    }
412
413    /// Specify additional `imports` to be evaluated.
414    ///
415    /// This function is usually only used to provide the first module.
416    pub fn import<I>(&mut self, imports: I)
417    where
418        I: Into<Imports<Ref>>,
419    {
420        self._import(imports.into())
421    }
422
423    fn _import(&mut self, imports: Imports<Ref>) {
424        let Imports(mut imports) = imports;
425        self.program.append(&mut imports);
426    }
427
428    /// Get the next module in the evaluation order.
429    ///
430    /// Modules are evaluated in DFS order.
431    #[expect(clippy::should_implement_trait)]
432    // This should not be implemented in [`Iterator::next`] because it does not
433    // really make sense for this type to be used as an [`Iterator`].
434    pub fn next(&mut self) -> Option<Ref> {
435        loop {
436            match self.program.pop_front()? {
437                Instruction::Enter(x) => {
438                    self.trace.push(x);
439                }
440                Instruction::Exit => {
441                    self.trace.pop();
442                }
443                Instruction::Eval(x) => break Some(x),
444            }
445        }
446    }
447
448    /// Finish the evaluation and get the result.
449    ///
450    /// Returns [`Some`] with the final value or [`None`] if no module has been
451    /// evaluated successfully and the [`Evaluator`] was created without an
452    /// initial value.
453    ///
454    /// See: [`Evaluator::with`].
455    pub fn finish(self) -> Option<Value> {
456        self.value
457    }
458}
459
460impl<Ref, Value> Evaluator<Ref, Value>
461where
462    Ref: Display,
463{
464    /// Build the module trace for `this` module.
465    ///
466    /// This function can be used to attach a module trace to [`module::Error`]
467    /// on any user-thrown errors during the evaluation loop.
468    ///
469    /// # Example
470    ///
471    /// ```rust,no_run
472    /// # use module::merge::Context;
473    /// # use module_util::evaluator::Evaluator;
474    /// # fn a() -> module_util::Result<i32> {
475    /// use module::Error;
476    ///
477    /// let mut evaluator = Evaluator::new();
478    ///
479    /// evaluator.import("module 1");
480    /// while let Some(this) = evaluator.next() {
481    ///     // ...
482    ///
483    ///     if this == "module 2" {
484    ///         return Err(Error::custom("module 2 is not allowed to be evaluated"))
485    ///             .with_trace(|| evaluator.trace(this));
486    ///     }
487    ///
488    ///     // ...
489    ///
490    /// #    let imports = unimplemented!();
491    /// #    let value: i32 = unimplemented!();
492    ///     evaluator.eval(this, imports, value)?;
493    /// }
494    ///
495    /// let value = evaluator.finish().unwrap();
496    /// Ok(value)
497    /// # }
498    /// ```
499    pub fn trace(&self, this: Ref) -> Trace {
500        let mut trace: Trace = self.trace.iter().collect();
501        trace.push_back(this);
502        trace
503    }
504}
505
506impl<Ref, Value> Evaluator<Ref, Value>
507where
508    Ref: Display,
509    Value: Merge,
510{
511    /// Evaluate the module `this`.
512    ///
513    /// - `imports`: any modules `this` imports
514    /// - `value`: the value of the `this` module
515    ///
516    /// Returns the result of the [`Merge`] operation on `value`. The returned
517    /// error has the appropriate trace attached.
518    ///
519    /// See: [`Evaluator::trace`].
520    pub fn eval(&mut self, this: Ref, imports: Imports<Ref>, value: Value) -> Result<()> {
521        self.value = match self.value.take() {
522            None => Some(value),
523            Some(old) => match old.merge(value) {
524                Ok(x) => Some(x),
525                Err(mut e) => {
526                    e.trace = self.trace(this);
527                    return Err(e);
528                }
529            },
530        };
531
532        let Imports(mut imports) = imports;
533        if !imports.is_empty() {
534            imports.push_front(Instruction::Enter(this));
535            imports.push_back(Instruction::Exit);
536            self.program.append_front(&mut imports);
537        }
538
539        Ok(())
540    }
541}
542
543#[cfg(test)]
544mod tests {
545    use module::types::NoMerge;
546    use module::{Context, Error};
547
548    use super::*;
549
550    fn eval<T>(initial: &'static str, modules: &[(&str, &[&'static str], T)]) -> Result<T>
551    where
552        T: Clone + Merge,
553    {
554        let mut x = Evaluator::new();
555
556        x.import(initial);
557        while let Some(this) = x.next() {
558            let Some((_, imports, value)) = modules.iter().find(|(name, _, _)| *name == this)
559            else {
560                return Err(Error::custom("no such module")).with_trace(|| x.trace(this));
561            };
562
563            let imports = imports.iter().copied().collect();
564            let value = value.clone();
565
566            x.eval(this, imports, value)?;
567        }
568
569        let value = x.finish().unwrap();
570        Ok(value)
571    }
572
573    #[test]
574    fn test_module_trace() {
575        let err = eval(
576            "module 1",
577            &[
578                ("module 1", &["module 2"], Some(NoMerge(()))),
579                ("module 2", &["module 3", "module 4"], None),
580                ("module 3", &[], None),
581                ("module 4", &["module 5"], None),
582                ("module 5", &["module 6"], None),
583                ("module 6", &[], Some(NoMerge(()))),
584            ],
585        )
586        .unwrap_err();
587
588        let trace: Vec<_> = err.trace.modules().collect();
589
590        assert_eq!(
591            trace,
592            &["module 1", "module 2", "module 4", "module 5", "module 6"]
593        );
594    }
595
596    #[test]
597    fn test_eval_order() {
598        // If the order is correct (DFS), then this should reach the
599        // "non-existent" module first and error there instead of erroring on
600        // "module 6" because of the collision.
601        let err = eval(
602            "module 1",
603            &[
604                ("module 1", &["module 2", "module 3"], Some(NoMerge(()))),
605                ("module 2", &["module 4"], None),
606                ("module 4", &["module 5"], None),
607                ("module 5", &["non-existent"], None),
608                ("module 3", &["module 6"], None),
609                ("module 6", &[], Some(NoMerge(()))),
610            ],
611        )
612        .unwrap_err();
613
614        assert!(!err.kind.is_collision());
615    }
616}