1use std::hash::Hash as StdHash;
5
6use crate::logs::{LogHeights, LogId, LogRanges, SeqNum, compare};
7use crate::traits::Author;
8
9#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq, StdHash)]
15pub struct Cursor<A, L> {
16 name: String,
17 state: LogHeights<A, L>,
18}
19
20impl<A, L> Cursor<A, L>
21where
22 A: Author,
23 L: LogId,
24{
25 pub fn new(name: impl AsRef<str>, state: LogHeights<A, L>) -> Self {
26 Self {
27 name: name.as_ref().to_string(),
28 state,
29 }
30 }
31
32 pub fn name(&self) -> &str {
33 &self.name
34 }
35
36 pub fn state(&self) -> &LogHeights<A, L> {
38 &self.state
39 }
40
41 pub fn log_height(&self, author: &A, log_id: &L) -> Option<&SeqNum> {
43 self.state.get(author).and_then(|logs| logs.get(log_id))
44 }
45
46 pub fn compare(&self, other: &LogHeights<A, L>) -> LogRanges<A, L> {
48 compare(other, &self.state)
49 }
50
51 pub fn advance(&mut self, author: A, log_id: L, log_height: SeqNum) {
53 if let Some(current_log_height) = self.log_height(&author, &log_id)
55 && current_log_height >= &log_height
56 {
57 return;
58 }
59
60 self.state
61 .entry(author)
62 .or_default()
63 .insert(log_id, log_height);
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use crate::logs::LogHeights;
70 use crate::{SigningKey, VerifyingKey};
71
72 use super::Cursor;
73
74 #[test]
75 fn advance_log_height() {
76 let author_1 = SigningKey::generate().verifying_key();
77 let author_2 = SigningKey::generate().verifying_key();
78
79 let mut cursor = Cursor::<VerifyingKey, u64>::new("test", LogHeights::default());
80 assert_eq!(cursor.name(), "test");
81
82 assert!(cursor.log_height(&author_1, &0).is_none());
83 assert!(cursor.log_height(&author_2, &0).is_none());
84
85 cursor.advance(author_1, 0, 23);
86 assert_eq!(cursor.log_height(&author_1, &0), Some(&23));
87 assert!(cursor.log_height(&author_2, &0).is_none());
88
89 cursor.advance(author_2, 0, 10);
90 cursor.advance(author_2, 1, 2);
91 assert_eq!(cursor.log_height(&author_1, &0), Some(&23));
92 assert_eq!(cursor.log_height(&author_2, &0), Some(&10));
93 assert_eq!(cursor.log_height(&author_2, &1), Some(&2));
94 }
95
96 #[test]
97 fn strict_monotonic_incremental() {
98 let author = SigningKey::generate().verifying_key();
99 let mut cursor = Cursor::<VerifyingKey, u64>::new("test", LogHeights::default());
100
101 cursor.advance(author, 0, 10);
103 cursor.advance(author, 0, 5);
104 assert_eq!(cursor.log_height(&author, &0), Some(&10));
105 }
106
107 #[test]
108 fn compare() {
109 let author = SigningKey::generate().verifying_key();
110 let log_id_1 = 1;
111 let log_id_2 = 2;
112
113 let mut cursor_1 = Cursor::<VerifyingKey, u64>::new("one", LogHeights::default());
114 let mut cursor_2 = Cursor::<VerifyingKey, u64>::new("two", LogHeights::default());
115
116 cursor_1.advance(author, log_id_1, 121);
117 cursor_1.advance(author, log_id_2, 13);
118 cursor_2.advance(author, log_id_1, 287);
119
120 let ranges = cursor_1.compare(cursor_2.state());
121 assert_eq!(
122 ranges.get(&author).unwrap().get(&log_id_1).unwrap(),
123 &(Some(121), Some(287))
124 );
125 assert!(ranges.get(&author).unwrap().get(&log_id_2).is_none());
126
127 let ranges = cursor_2.compare(cursor_1.state());
128 assert!(ranges.get(&author).unwrap().get(&log_id_1).is_none());
129 assert_eq!(
130 ranges.get(&author).unwrap().get(&log_id_2).unwrap(),
131 &(None, Some(13))
132 );
133 }
134}