Skip to main content

coven_merge/
dependency.rs

1//! Original sources consumed by a row change (§9.5, D5).
2
3/// The changes one row change used, in role order: the generation source
4/// first, then each replaced setter in the authored column order, then the
5/// sources its operations used beyond those, then its foreign-key causes in
6/// execution order. A source that fills several roles appears once per role.
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct Provenance<C> {
9    /// The change that established the generation this change advanced.
10    pub generation: Option<C>,
11    /// The change that set each cell this change replaced.
12    pub setters: Vec<C>,
13    /// The generation source and replaced setters its operations used that
14    /// the net change does not already name (D5 `Execution`).
15    pub execution: Vec<C>,
16    /// The direct changes and intermediate actions in the same write whose
17    /// foreign-key actions produced this change (§9.6).
18    pub causes: Vec<C>,
19}
20
21impl<C> Provenance<C> {
22    /// Every source, in role order, keeping repeats.
23    pub fn sources(&self) -> impl Iterator<Item = &C> {
24        self.generation.iter().chain(&self.setters).chain(&self.execution).chain(&self.causes)
25    }
26}
27
28/// One row change's verdict under the shared rule.
29#[derive(Clone, Debug, PartialEq, Eq)]
30#[cfg(test)]
31pub(crate) enum DataVerdict<C> {
32    /// It applies.
33    Retained,
34    /// Its own admission dropped it: a removal cut-off, schema change or
35    /// reset. That policy decides whether it leaves a loss.
36    Dropped,
37    /// It was admitted but used data that does not survive. It becomes a
38    /// frozen loss naming these failed sources in role order, with repeats.
39    Dependent(Vec<C>),
40}
41
42#[cfg(test)]
43impl<C> DataVerdict<C> {
44    /// Whether this change survives as a source for later changes.
45    pub(crate) fn retained(&self) -> bool {
46        matches!(self, Self::Retained)
47    }
48}
49
50/// Classify a change from its own admission and its sources' verdicts.
51/// Sources were decided first: each was authored, and so consumed, earlier.
52/// `retained` answers for one source; an unavailable source is the caller's
53/// wait or refusal, never a guess here.
54#[cfg(test)]
55pub(crate) fn classify<C: Clone, E>(
56    admitted: bool,
57    provenance: &Provenance<C>,
58    mut retained: impl FnMut(&C) -> Result<bool, E>,
59) -> Result<DataVerdict<C>, E> {
60    if !admitted {
61        return Ok(DataVerdict::Dropped);
62    }
63    let mut failed = Vec::new();
64    for source in provenance.sources() {
65        if !retained(source)? {
66            failed.push(source.clone());
67        }
68    }
69    Ok(if failed.is_empty() { DataVerdict::Retained } else { DataVerdict::Dependent(failed) })
70}
71
72#[cfg(test)]
73#[path = "dependency_tests.rs"]
74mod tests;