sim_incremental_core/projection/
graph.rs1use std::{
2 collections::{BTreeMap, BTreeSet},
3 error::Error,
4 fmt,
5};
6
7use super::{ConclusionId, Explanation, FactId};
8
9#[derive(Clone, Debug, Eq, PartialEq)]
11pub struct OwnerProjectionGraph {
12 owner: String,
13 dependencies: BTreeMap<ConclusionId, BTreeSet<FactId>>,
14}
15
16impl OwnerProjectionGraph {
17 pub fn new(
19 owner: impl Into<String>,
20 dependencies: impl IntoIterator<Item = (ConclusionId, BTreeSet<FactId>)>,
21 ) -> Result<Self, ClosureError> {
22 let owner = owner.into();
23 if owner.trim().is_empty() {
24 return Err(ClosureError::EmptyOwner);
25 }
26 let mut canonical = BTreeMap::new();
27 for (conclusion, facts) in dependencies {
28 if facts.is_empty() {
29 return Err(ClosureError::ConclusionWithoutFacts(conclusion));
30 }
31 if canonical.insert(conclusion.clone(), facts).is_some() {
32 return Err(ClosureError::DuplicateConclusion(conclusion));
33 }
34 }
35 if canonical.is_empty() {
36 return Err(ClosureError::EmptyOwnerGraph(owner));
37 }
38 Ok(Self {
39 owner,
40 dependencies: canonical,
41 })
42 }
43
44 #[must_use]
46 pub fn owner(&self) -> &str {
47 &self.owner
48 }
49}
50
51#[derive(Clone, Debug, Eq, PartialEq)]
53pub struct FederatedClosure {
54 owners: BTreeMap<ConclusionId, String>,
55 dependencies: BTreeMap<ConclusionId, BTreeSet<FactId>>,
56 consumers: BTreeMap<FactId, BTreeSet<ConclusionId>>,
57}
58
59impl FederatedClosure {
60 pub fn seal(
62 facts: impl IntoIterator<Item = FactId>,
63 graphs: impl IntoIterator<Item = OwnerProjectionGraph>,
64 ) -> Result<Self, ClosureError> {
65 let facts = facts.into_iter().collect::<BTreeSet<_>>();
66 let mut owners = BTreeMap::new();
67 let mut dependencies = BTreeMap::new();
68 let mut consumers = facts
69 .iter()
70 .cloned()
71 .map(|fact| (fact, BTreeSet::new()))
72 .collect::<BTreeMap<_, _>>();
73 for graph in graphs {
74 for (conclusion, required) in graph.dependencies {
75 if let Some(first) = owners.insert(conclusion.clone(), graph.owner.clone()) {
76 return Err(ClosureError::DuplicateOwner {
77 conclusion,
78 first,
79 second: graph.owner,
80 });
81 }
82 for fact in &required {
83 let Some(fact_consumers) = consumers.get_mut(fact) else {
84 return Err(ClosureError::UnknownFact {
85 conclusion,
86 fact: fact.clone(),
87 });
88 };
89 fact_consumers.insert(conclusion.clone());
90 }
91 dependencies.insert(conclusion, required);
92 }
93 }
94 if dependencies.is_empty() {
95 return Err(ClosureError::EmptyClosure);
96 }
97 Ok(Self {
98 owners,
99 dependencies,
100 consumers,
101 })
102 }
103
104 #[must_use]
106 pub fn affected(&self, changed: impl IntoIterator<Item = FactId>) -> Vec<ConclusionId> {
107 changed
108 .into_iter()
109 .filter_map(|fact| self.consumers.get(&fact))
110 .flatten()
111 .cloned()
112 .collect::<BTreeSet<_>>()
113 .into_iter()
114 .collect()
115 }
116
117 pub fn explain(
119 &self,
120 conclusion: &ConclusionId,
121 fact: &FactId,
122 ) -> Result<Explanation, ClosureError> {
123 let Some(required) = self.dependencies.get(conclusion) else {
124 return Err(ClosureError::UnknownConclusion(conclusion.clone()));
125 };
126 if !required.contains(fact) {
127 return Err(ClosureError::Unrelated {
128 conclusion: conclusion.clone(),
129 fact: fact.clone(),
130 });
131 }
132 Ok(Explanation {
133 conclusion: conclusion.clone(),
134 fact: fact.clone(),
135 path: vec![
136 format!("owner/{}", self.owners[conclusion]),
137 format!("conclusion/{conclusion}"),
138 format!("fact/{fact}"),
139 ],
140 })
141 }
142}
143
144#[derive(Clone, Debug, Eq, PartialEq)]
146pub enum ClosureError {
147 EmptyOwner,
149 EmptyOwnerGraph(String),
151 ConclusionWithoutFacts(ConclusionId),
153 DuplicateConclusion(ConclusionId),
155 DuplicateOwner {
157 conclusion: ConclusionId,
159 first: String,
161 second: String,
163 },
164 UnknownFact {
166 conclusion: ConclusionId,
168 fact: FactId,
170 },
171 EmptyClosure,
173 UnknownConclusion(ConclusionId),
175 Unrelated {
177 conclusion: ConclusionId,
179 fact: FactId,
181 },
182}
183
184impl fmt::Display for ClosureError {
185 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(formatter, "{self:?}")
187 }
188}
189
190impl Error for ClosureError {}