Skip to main content

coven_format/
dismissal.rs

1//! Lost-value acknowledgements in ordinary write streams (format.md D5).
2
3use crate::error::{require, Error, Rule};
4use crate::value::{name, positive, row};
5use crate::wire::{decode_frame, decode_frame_value, wire_struct, Decoder, Encoder, Wire};
6use coven_merge::{RowId, WriteId, WritePast};
7
8/// What the author acknowledged. Empty frozen rows have no cell to name.
9#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
10pub enum DismissalTarget {
11    /// A column containing a lost value.
12    Cell(String),
13    /// A frozen original change with no retained values.
14    EmptyRow,
15}
16
17impl Wire for DismissalTarget {
18    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
19        match self {
20            Self::Cell(column) => {
21                0u8.put(out)?;
22                column.put(out)
23            }
24            Self::EmptyRow => 1u8.put(out),
25        }
26    }
27    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
28        match u8::get(input)? {
29            0 => crate::wire::get_name(input).map(Self::Cell),
30            1 => Ok(Self::EmptyRow),
31            tag => Err(Error::UnknownTag { field: "dismissal target", tag }),
32        }
33    }
34}
35
36/// One lost value the author has dealt with. Removed rows use ordinary deletes.
37#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
38pub struct Dismissal {
39    /// The table, key and audience containing the loss.
40    pub row: RowId,
41    /// A lost cell or an empty frozen row.
42    pub target: DismissalTarget,
43    /// The cell's setter, or the empty row's original write.
44    pub write: WriteId,
45}
46wire_struct!(Dismissal, row, target, write);
47
48impl Dismissal {
49    /// Encode a bounded kind-3 dismissal frame.
50    pub fn encode(&self) -> Result<Vec<u8>, Error> {
51        self.validate()?;
52        crate::encode_frame(crate::kinds::DISMISSAL, self)
53    }
54
55    /// Decode exactly one dismissal, checking names and write identity.
56    pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
57        let dismissal: Self = decode_frame_value(bytes, crate::kinds::DISMISSAL, "dismissal kind")?;
58        dismissal.validate()?;
59        Ok(dismissal)
60    }
61
62    /// A dismissal can acknowledge only a value its author had read.
63    pub fn validate_past(&self, past: &impl WritePast) -> Result<(), Error> {
64        require(past.contains(&self.write), "dismissed write", Rule::Coverage)
65    }
66
67    pub(crate) fn validate(&self) -> Result<(), Error> {
68        row(&self.row)?;
69        if let DismissalTarget::Cell(column) = &self.target {
70            name(column)?;
71        }
72        positive(self.write.number)
73    }
74}
75
76/// One decoded write-part frame; a row's change or execution precedes
77/// dismissals of that row.
78#[derive(Clone, Debug, PartialEq, Eq)]
79pub enum WriteFrame {
80    /// An insert, update or delete of a row.
81    Change(crate::write::RowChange),
82    /// SQL operations on a row with no net change.
83    Execution(crate::write::RowExecution),
84    /// An acknowledgement of a lost cell or an empty frozen row.
85    Dismissal(Dismissal),
86}
87
88impl WriteFrame {
89    /// Encode this record with its kind and bounded payload.
90    pub fn encode(&self) -> Result<Vec<u8>, Error> {
91        match self {
92            Self::Change(row) => row.encode(),
93            Self::Execution(execution) => execution.encode(),
94            Self::Dismissal(dismissal) => dismissal.encode(),
95        }
96    }
97
98    /// The row record this frame carries, as a part's evidence names it;
99    /// none for a dismissal.
100    pub fn record(&self) -> Option<crate::write::PartRecord<'_>> {
101        match self {
102            Self::Change(row) => Some(crate::write::PartRecord::Change(row)),
103            Self::Execution(execution) => Some(crate::write::PartRecord::Execution(execution)),
104            Self::Dismissal(_) => None,
105        }
106    }
107
108    /// Decode a row change, execution or dismissal, refusing other frame kinds.
109    pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
110        let (kind, _) = decode_frame(bytes)?;
111        match kind {
112            crate::kinds::ROW_CHANGE => crate::write::RowChange::decode(bytes).map(Self::Change),
113            crate::kinds::DISMISSAL => Dismissal::decode(bytes).map(Self::Dismissal),
114            crate::kinds::ROW_EXECUTION => {
115                crate::write::RowExecution::decode(bytes).map(Self::Execution)
116            }
117            tag => Err(Error::UnknownTag { field: "write record kind", tag }),
118        }
119    }
120
121    pub(crate) fn key(&self) -> (RowId, Option<(DismissalTarget, WriteId)>) {
122        match self {
123            Self::Change(row) => (row.row.clone(), None),
124            Self::Execution(execution) => (execution.row.clone(), None),
125            Self::Dismissal(dismissal) => {
126                (dismissal.row.clone(), Some((dismissal.target.clone(), dismissal.write)))
127            }
128        }
129    }
130}
131
132#[cfg(test)]
133#[path = "dismissal_tests.rs"]
134mod tests;