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