Skip to main content

trellis_core/
scope_lifecycle.rs

1use crate::{Graph, GraphResult, ResourceKey, ScopeId};
2use std::collections::BTreeSet;
3
4impl<C> Graph<C> {
5    pub(crate) fn close_scope_direct(&mut self, scope: ScopeId) -> GraphResult<Vec<ScopeId>> {
6        self.require_scope(scope)?;
7        let scopes = self.scope_close_order(scope);
8        for closing in &scopes {
9            if let Some(scope_meta) = self.scopes.get_mut(closing) {
10                scope_meta.close();
11            }
12            self.resource_planners
13                .retain(|planner| planner.scope != *closing);
14        }
15        Ok(scopes)
16    }
17
18    pub(crate) fn reclaim_closed_scopes(&mut self, closed_scopes: &[ScopeId]) {
19        let reclaimed_nodes = self.reclaim_closed_scope_nodes(closed_scopes);
20        self.reclaim_closed_scope_metadata(closed_scopes);
21        if !reclaimed_nodes.is_empty() || !closed_scopes.is_empty() {
22            self.invalidate_topology_cache();
23        }
24    }
25
26    /// Returns child scopes in stable id order.
27    pub fn child_scopes(&self, scope: ScopeId) -> GraphResult<Vec<ScopeId>> {
28        self.require_scope(scope)?;
29        Ok(self.child_scopes_unchecked(scope))
30    }
31
32    /// Returns resources whose owner set is empty or contains no live scope.
33    pub fn orphan_resources(&self) -> Vec<ResourceKey> {
34        self.resource_owners
35            .iter()
36            .filter_map(|(key, owners)| {
37                let has_live_owner = owners
38                    .iter()
39                    .any(|scope| self.scopes.get(scope).is_some_and(|meta| !meta.is_closed()));
40                (!has_live_owner).then(|| key.clone())
41            })
42            .collect()
43    }
44
45    fn scope_close_order(&self, scope: ScopeId) -> Vec<ScopeId> {
46        let mut scopes = Vec::new();
47        let mut stack = vec![ScopeVisitFrame::Enter(scope)];
48
49        while let Some(frame) = stack.pop() {
50            match frame {
51                ScopeVisitFrame::Exit(scope) => {
52                    if self
53                        .scopes
54                        .get(&scope)
55                        .is_some_and(|scope_meta| !scope_meta.is_closed())
56                    {
57                        scopes.push(scope);
58                    }
59                }
60                ScopeVisitFrame::Enter(scope) => {
61                    stack.push(ScopeVisitFrame::Exit(scope));
62                    let mut children = self.child_scopes_unchecked(scope);
63                    children.reverse();
64                    for child in children {
65                        stack.push(ScopeVisitFrame::Enter(child));
66                    }
67                }
68            }
69        }
70
71        scopes
72    }
73
74    fn child_scopes_unchecked(&self, scope: ScopeId) -> Vec<ScopeId> {
75        self.scope_children
76            .get(&scope)
77            .map(|children| children.iter().copied().collect())
78            .unwrap_or_default()
79    }
80
81    fn reclaim_closed_scope_nodes(&mut self, closed_scopes: &[ScopeId]) -> Vec<crate::NodeId> {
82        let closed_scopes = closed_scopes.iter().copied().collect::<BTreeSet<_>>();
83        let nodes = self
84            .nodes
85            .values()
86            .filter_map(|node| {
87                node.owning_scope()
88                    .filter(|scope| closed_scopes.contains(scope))
89                    .map(|_| node.id())
90            })
91            .collect::<Vec<_>>();
92        for node in &nodes {
93            self.remove_node_storage(*node);
94        }
95        nodes
96    }
97
98    fn remove_node_storage(&mut self, node: crate::NodeId) {
99        self.nodes.remove(&node);
100        self.input_values.remove(&node);
101        self.derived_specs.remove(&node);
102        self.derived_values.remove(&node);
103        self.collection_specs.remove(&node);
104        self.collection_values.remove(&node);
105        self.previous_collection_values.remove(&node);
106        self.collection_diffs.remove(&node);
107        self.resource_planners
108            .retain(|planner| planner.collection != node);
109        self.audit.node_changes.remove(&node);
110    }
111
112    fn reclaim_closed_scope_metadata(&mut self, closed_scopes: &[ScopeId]) {
113        for scope in closed_scopes {
114            if let Some(scope_meta) = self.scopes.remove(scope)
115                && let Some(parent) = scope_meta.parent()
116            {
117                let remove_parent = if let Some(children) = self.scope_children.get_mut(&parent) {
118                    children.remove(scope);
119                    children.is_empty()
120                } else {
121                    false
122                };
123                if remove_parent {
124                    self.scope_children.remove(&parent);
125                }
126            }
127            self.scope_children.remove(scope);
128        }
129    }
130}
131
132enum ScopeVisitFrame {
133    Enter(ScopeId),
134    Exit(ScopeId),
135}