scopegraphs_lib/completeness/explicit.rs
1use crate::completeness::FutureCompleteness;
2use crate::{
3 completeness::private::Sealed,
4 completeness::{
5 Completeness, CriticalEdgeBasedCompleteness, CriticalEdgeSet, Delay, EdgeClosedError,
6 EdgesOrDelay,
7 },
8 label::Label,
9 InnerScopeGraph, Scope, ScopeGraph,
10};
11use std::{collections::HashSet, hash::Hash};
12
13/// Critical-edge based [`Completeness`] implementation.
14///
15/// Unlike [`ImplicitClose`], this implementation shifts responsibility of closing edges to the
16/// _type checker writer_. I.e., they have to insert `sg.close(scope, label)` statements at the
17/// appropriate positions in the code.
18///
19/// Returns [`EdgeClosedError`] when an edge is added to a scope in which the label is already
20/// closed (by an explicit close of the type checker writer).
21///
22/// Returns [`Delay`] when edges are retrieved (e.g. during query resolution) for an edge that is
23/// not yet closed.
24#[derive(Debug)]
25pub struct ExplicitClose<LABEL> {
26 critical_edges: CriticalEdgeSet<LABEL>,
27}
28
29impl<LABEL> Default for ExplicitClose<LABEL> {
30 fn default() -> Self {
31 ExplicitClose {
32 critical_edges: CriticalEdgeSet::default(),
33 }
34 }
35}
36
37impl<LABEL> Sealed for ExplicitClose<LABEL> {}
38
39impl<LABEL: Hash + Eq + Label, DATA> Completeness<LABEL, DATA> for ExplicitClose<LABEL> {
40 fn cmpl_new_scope(&self, _: &InnerScopeGraph<LABEL, DATA>, _: Scope) {
41 <ExplicitClose<LABEL> as CriticalEdgeBasedCompleteness<LABEL, DATA>>::init_scope_with(
42 self,
43 LABEL::iter().collect(), // init with all labels: programmer is responsible for closing edges
44 )
45 }
46
47 fn cmpl_new_complete_scope(&self, _: &InnerScopeGraph<LABEL, DATA>, _: Scope) {
48 <ExplicitClose<LABEL> as CriticalEdgeBasedCompleteness<LABEL, DATA>>::init_scope_with(
49 self,
50 HashSet::new(), // init with empty label set to prevent extension
51 )
52 }
53
54 type NewEdgeResult = Result<(), EdgeClosedError<LABEL>>;
55
56 fn cmpl_new_edge(
57 &self,
58 inner_scope_graph: &InnerScopeGraph<LABEL, DATA>,
59 src: Scope,
60 lbl: LABEL,
61 dst: Scope,
62 ) -> Self::NewEdgeResult {
63 if self.critical_edges.is_open(src, &lbl) {
64 inner_scope_graph.add_edge(src, lbl, dst);
65 Ok(())
66 } else {
67 Err(EdgeClosedError {
68 scope: src,
69 label: lbl,
70 })
71 }
72 }
73
74 type GetEdgesResult<'rslv> = EdgesOrDelay<Vec<Scope>, LABEL>
75 where
76 Self: 'rslv, LABEL: 'rslv, DATA: 'rslv;
77
78 fn cmpl_get_edges<'rslv>(
79 &self,
80 inner_scope_graph: &InnerScopeGraph<LABEL, DATA>,
81 src: Scope,
82 lbl: LABEL,
83 ) -> Self::GetEdgesResult<'rslv>
84 where
85 LABEL: 'rslv,
86 DATA: 'rslv,
87 {
88 if self.critical_edges.is_open(src, &lbl) {
89 Err(Delay {
90 scope: src,
91 label: lbl,
92 })
93 } else {
94 Ok(inner_scope_graph.get_edges(src, lbl))
95 }
96 }
97}
98
99impl<LABEL: Hash + Eq + Label, DATA> CriticalEdgeBasedCompleteness<LABEL, DATA>
100 for ExplicitClose<LABEL>
101{
102 fn init_scope_with(&self, open_labels: HashSet<LABEL>) {
103 self.critical_edges.init_scope(open_labels)
104 }
105}
106
107impl<LABEL: Hash + Eq> ExplicitClose<LABEL> {
108 pub(super) fn close(&self, scope: Scope, label: &LABEL) {
109 self.critical_edges.close(scope, label);
110 }
111}
112
113impl<'sg, LABEL: Hash + Eq, DATA> ScopeGraph<'sg, LABEL, DATA, ExplicitClose<LABEL>> {
114 /// Closes an edge, (i.e., prohibit future new
115 ///
116 /// For example, the following program will return an error.
117 /// ```
118 /// # use scopegraphs_lib::completeness::ExplicitClose;
119 /// # use scopegraphs_lib::ScopeGraph;
120 /// # use scopegraphs_macros::Label;
121 /// # use scopegraphs::storage::Storage;
122 ///
123 /// # #[derive(Eq, Hash, PartialEq, Label)] enum Lbl { Def }
124 /// # use Lbl::*;
125 /// let storage = Storage::new();
126 /// let mut sg = ScopeGraph::<Lbl, usize, _>::new(&storage, ExplicitClose::default());
127 ///
128 /// let s1 = sg.add_scope_with(0, [Def]);
129 /// let s2 = sg.add_scope_closed(42);
130 ///
131 /// sg.close(s1, &Def);
132 /// sg.add_edge(s1, Def, s2).expect_err("cannot add edge after closing edge");
133 /// ```
134 ///
135 /// Closing is required to permit queries to traverse these edges:
136 /// ```
137 ///
138 /// # use scopegraphs_lib::completeness::ExplicitClose;
139 /// # use scopegraphs_lib::ScopeGraph;
140 /// # use scopegraphs_lib::resolve::{DefaultDataEquiv, DefaultLabelOrder, EdgeOrData, Resolve};
141 /// # use scopegraphs_macros::{compile_regex, Label};
142 /// # use scopegraphs::storage::Storage;
143 /// #
144 /// # #[derive(Eq, Hash, PartialEq, Label, Debug, Copy, Clone)]
145 /// # enum Lbl { Def }
146 /// # use Lbl::*;
147 /// # type LblD = EdgeOrData<Lbl>;
148 /// #
149 /// # compile_regex!(type Regex<Lbl> = Def);
150 /// let storage = Storage::new();
151 /// let mut sg = ScopeGraph::<Lbl, usize, _>::new(&storage, ExplicitClose::default());
152 ///
153 /// let s1 = sg.add_scope_with(0, [Def]);
154 /// let s2 = sg.add_scope_closed(42);
155 ///
156 /// // Note: not calling `sg.close(s1, &Def)`
157 ///
158 /// let query_result = sg.query()
159 /// .with_path_wellformedness(Regex::new()) // regex: `Def`
160 /// .with_data_wellformedness(|x: &usize| *x == 42) // match `42`
161 /// .resolve(s1);
162 ///
163 /// query_result.expect_err("require s1/Def to be closed");
164 /// ```
165 ///
166 /// Closing allows queries to resolve:
167 /// ```
168 ///
169 /// # use scopegraphs_lib::completeness::ExplicitClose;
170 /// # use scopegraphs_lib::ScopeGraph;
171 /// # use scopegraphs_lib::resolve::{DefaultDataEquiv, DefaultLabelOrder, EdgeOrData, Resolve};
172 /// # use scopegraphs_macros::{compile_regex, Label};
173 /// # use scopegraphs_lib::storage::Storage;
174 /// #
175 /// # #[derive(Eq, Hash, PartialEq, Label, Debug, Copy, Clone)]
176 /// # enum Lbl { Def }
177 /// # use Lbl::*;
178 /// # type LblD = EdgeOrData<Lbl>;
179 /// #
180 /// # compile_regex!(type Regex<Lbl> = Def);
181 /// let storage = Storage::new();
182 /// let mut sg = ScopeGraph::<Lbl, usize, _>::new(&storage, ExplicitClose::default());
183 ///
184 /// let s1 = sg.add_scope_with(0, [Def]);
185 /// let s2 = sg.add_scope_closed(42);
186 ///
187 /// // Note: closing the edge *after* creating all edges, *before* doing the query
188 /// sg.close(s1, &Def);
189 ///
190 /// let query_result = sg.query()
191 /// .with_path_wellformedness(Regex::new()) // regex: `Def`
192 /// .with_data_wellformedness(|x: &usize| *x == 42) // match `42`
193 /// .resolve(s1);
194 ///
195 /// query_result.expect("query should return result");
196 /// ```
197 pub fn close(&self, scope: Scope, label: &LABEL) {
198 self.completeness.close(scope, label)
199 }
200}
201
202impl<'sg, LABEL: Hash + Eq + Copy, DATA> ScopeGraph<'sg, LABEL, DATA, FutureCompleteness<LABEL>> {
203 /// TODO: update this example to use futures
204 /// Closes an edge, (i.e., prohibit future new
205 ///
206 /// For example, the following program will return an error.
207 /// ```
208 /// # use scopegraphs_lib::completeness::ExplicitClose;
209 /// # use scopegraphs_lib::{ScopeGraph, storage};
210 /// # use scopegraphs_macros::Label;
211 /// # use scopegraphs_lib::storage::Storage;
212 /// # #[derive(Eq, Hash, PartialEq, Label)] enum Lbl { Def }
213 /// # use Lbl::*;
214 /// let storage = Storage::new();
215 /// let mut sg = ScopeGraph::<Lbl, usize, _>::new(&storage, ExplicitClose::default());
216 ///
217 /// let s1 = sg.add_scope_with(0, [Def]);
218 /// let s2 = sg.add_scope_closed(42);
219 ///
220 /// sg.close(s1, &Def);
221 /// sg.add_edge(s1, Def, s2).expect_err("cannot add edge after closing edge");
222 /// ```
223 ///
224 /// Closing is required to permit queries to traverse these edges:
225 /// ```
226 ///
227 /// # use scopegraphs_lib::completeness::ExplicitClose;
228 /// # use scopegraphs_lib::ScopeGraph;
229 /// # use scopegraphs_lib::resolve::{DefaultDataEquiv, DefaultLabelOrder, EdgeOrData, Resolve};
230 /// # use scopegraphs_macros::{compile_regex, Label};
231 /// # use scopegraphs_lib::storage::Storage;
232 /// #
233 /// # #[derive(Eq, Hash, PartialEq, Label, Debug, Copy, Clone)]
234 /// # enum Lbl { Def }
235 /// # use Lbl::*;
236 /// # type LblD = EdgeOrData<Lbl>;
237 /// #
238 /// # compile_regex!(type Regex<Lbl> = Def);
239 /// let storage = Storage::new();
240 /// let mut sg = ScopeGraph::<Lbl, usize, _>::new(&storage, ExplicitClose::default());
241 ///
242 /// let s1 = sg.add_scope_with(0, [Def]);
243 /// let s2 = sg.add_scope_closed(42);
244 ///
245 /// // Note: not calling `sg.close(s1, &Def)`
246 ///
247 /// let query_result = sg.query()
248 /// .with_path_wellformedness(Regex::new()) // regex: `Def`
249 /// .with_data_wellformedness(|x: &usize| *x == 42) // match `42`
250 /// .resolve(s1);
251 ///
252 /// query_result.expect_err("require s1/Def to be closed");
253 /// ```
254 ///
255 /// Closing allows queries to resolve:
256 /// ```
257 ///
258 /// # use scopegraphs_lib::completeness::ExplicitClose;
259 /// # use scopegraphs_lib::ScopeGraph;
260 /// # use scopegraphs_lib::resolve::{DefaultDataEquiv, DefaultLabelOrder, EdgeOrData, Resolve};
261 /// # use scopegraphs_macros::{compile_regex, Label};
262 /// # use scopegraphs_lib::storage::Storage;
263 /// #
264 /// # #[derive(Eq, Hash, PartialEq, Label, Debug, Copy, Clone)]
265 /// # enum Lbl { Def }
266 /// # use Lbl::*;
267 /// # type LblD = EdgeOrData<Lbl>;
268 /// #
269 /// # compile_regex!(type Regex<Lbl> = Def);
270 /// let storage = Storage::new();
271 /// let mut sg = ScopeGraph::<Lbl, usize, _>::new(&storage, ExplicitClose::default());
272 ///
273 /// let s1 = sg.add_scope_with(0, [Def]);
274 /// let s2 = sg.add_scope_closed(42);
275 ///
276 /// // Note: closing the edge *after* creating all edges, *before* doing the query
277 /// sg.close(s1, &Def);
278 ///
279 /// let query_result = sg.query()
280 /// .with_path_wellformedness(Regex::new()) // regex: `Def`
281 /// .with_data_wellformedness(|x: &usize| *x == 42) // match `42`
282 /// .resolve(s1);
283 ///
284 /// query_result.expect("query should return result");
285 /// ```
286 pub fn close(&self, scope: Scope, label: &LABEL) {
287 self.completeness.close(scope, label)
288 }
289}