Skip to main content

cortexkit_usage_steps/
latest.rs

1use std::collections::HashMap;
2
3use crate::{Cursor, ReadReply, StepRow};
4
5/// What [`LatestRows::apply`] did with a row.
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7#[non_exhaustive]
8pub enum Applied {
9    /// The first row seen for its identity.
10    Added,
11    /// A row with the same identity was already kept and is now replaced.
12    Replaced,
13}
14
15/// The last row per identity (`harness` plus `step_ref`), in the order reads
16/// returned them.
17///
18/// Apply rows in the order the reads return them, which is cursor order:
19/// pages oldest first, one read at a time, each from the previous read's
20/// [`ReadReply::resume_after`]. A later row for an identity replaces the kept
21/// one in place; it is never appended. Re-applying a row already seen is
22/// harmless, because it replaces itself. Cursors are never compared.
23#[derive(Clone, Debug, Default)]
24pub struct LatestRows {
25    rows: Vec<StepRow>,
26    index: HashMap<(String, String), usize>,
27    resume_after: Option<Cursor>,
28}
29
30impl LatestRows {
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Keeps `row` as the latest for its identity.
36    pub fn apply(&mut self, row: StepRow) -> Applied {
37        let key = (row.harness.clone(), row.step_ref.clone());
38        match self.index.get(&key) {
39            Some(&slot) => {
40                self.rows[slot] = row;
41                Applied::Replaced
42            }
43            None => {
44                self.index.insert(key, self.rows.len());
45                self.rows.push(row);
46                Applied::Added
47            }
48        }
49    }
50
51    /// Applies a reply's rows in order and records where the next read
52    /// starts.
53    pub fn apply_reply(&mut self, reply: ReadReply) {
54        self.resume_after = Some(reply.resume_after().clone());
55        for row in reply.rows {
56            self.apply(row);
57        }
58    }
59
60    /// The cursor to pass as the next read's `after`, once a reply was
61    /// applied.
62    pub fn resume_after(&self) -> Option<&Cursor> {
63        self.resume_after.as_ref()
64    }
65
66    /// The kept row for an identity.
67    pub fn get(&self, harness: &str, step_ref: &str) -> Option<&StepRow> {
68        self.index
69            .get(&(harness.to_owned(), step_ref.to_owned()))
70            .map(|&slot| &self.rows[slot])
71    }
72
73    /// Kept rows, one per identity, in the order each identity was first
74    /// seen.
75    pub fn iter(&self) -> impl Iterator<Item = &StepRow> {
76        self.rows.iter()
77    }
78
79    pub fn len(&self) -> usize {
80        self.rows.len()
81    }
82
83    pub fn is_empty(&self) -> bool {
84        self.rows.is_empty()
85    }
86
87    /// Forgets every row and the resume cursor, as after `cursor_expired`.
88    pub fn clear(&mut self) {
89        *self = Self::default();
90    }
91}