conjure_core/rule_engine/
submodel_zipper.rs

1#![allow(dead_code)]
2use std::sync::Arc;
3
4use uniplate::zipper::Zipper;
5
6use crate::ast::{Expression, SubModel};
7
8/// Traverses expressions in this sub-model, but not into inner sub-models.
9///
10/// Same types and usage as `Biplate::contexts_bi`.
11pub(super) fn submodel_ctx(
12    m: SubModel,
13) -> impl Iterator<Item = (Expression, Arc<dyn Fn(Expression) -> SubModel>)> {
14    SubmodelCtx {
15        zipper: SubmodelZipper {
16            inner: Zipper::new(m.root().clone()),
17        },
18        submodel: m.clone(),
19        done: false,
20    }
21}
22
23/// A zipper that traverses over the current submodel only, and does not traverse into nested
24/// scopes.
25#[derive(Clone)]
26struct SubmodelZipper {
27    inner: Zipper<Expression>,
28}
29
30impl SubmodelZipper {
31    fn go_left(&mut self) -> Option<()> {
32        self.inner.go_left()
33    }
34
35    fn go_right(&mut self) -> Option<()> {
36        self.inner.go_right()
37    }
38
39    fn go_up(&mut self) -> Option<()> {
40        self.inner.go_up()
41    }
42
43    fn rebuild_root(self) -> Expression {
44        self.inner.rebuild_root()
45    }
46
47    fn go_down(&mut self) -> Option<()> {
48        if matches!(self.inner.focus(), Expression::Scope(_, _)) {
49            None
50        } else {
51            self.inner.go_down()
52        }
53    }
54
55    fn focus(&self) -> &Expression {
56        self.inner.focus()
57    }
58
59    fn focus_mut(&mut self) -> &mut Expression {
60        self.inner.focus_mut()
61    }
62}
63
64pub struct SubmodelCtx {
65    zipper: SubmodelZipper,
66    submodel: SubModel,
67    done: bool,
68}
69
70impl Iterator for SubmodelCtx {
71    type Item = (Expression, Arc<dyn Fn(Expression) -> SubModel>);
72
73    fn next(&mut self) -> Option<Self::Item> {
74        if self.done {
75            return None;
76        }
77        let node = self.zipper.focus().clone();
78        let submodel = self.submodel.clone();
79        let zipper = self.zipper.clone();
80
81        #[allow(clippy::arc_with_non_send_sync)]
82        let ctx = Arc::new(move |x| {
83            let mut zipper2 = zipper.clone();
84            *zipper2.focus_mut() = x;
85            let root = zipper2.rebuild_root();
86            let mut submodel2 = submodel.clone();
87            submodel2.replace_root(root);
88            submodel2
89        });
90
91        // prepare iterator for next element.
92        // try moving down or right. if we can't move up the tree until we can move right.
93        if self.zipper.go_down().is_none() {
94            while self.zipper.go_right().is_none() {
95                if self.zipper.go_up().is_none() {
96                    // at the top again, so this will be the last time we return a node
97                    self.done = true;
98                    break;
99                };
100            }
101        }
102
103        Some((node, ctx))
104    }
105}