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}