1use std::collections::BTreeMap;
5use std::hash::Hash as StdHash;
6
7use serde::{Deserialize, Serialize};
8
9use crate::traits::Author;
10
11pub trait LogId: Clone + Eq + Ord + StdHash + Serialize + for<'de> Deserialize<'de> {}
36
37impl<T> LogId for T where T: Clone + Eq + Ord + StdHash + Serialize + for<'de> Deserialize<'de> {}
38
39pub type SeqNum = u32;
41
42pub type LogHeights<A, L> = BTreeMap<A, BTreeMap<L, SeqNum>>;
44
45pub type LogRanges<A, L> = BTreeMap<A, BTreeMap<L, (Option<SeqNum>, Option<SeqNum>)>>;
47
48pub fn compare<A, L>(local: &LogHeights<A, L>, remote: &LogHeights<A, L>) -> LogRanges<A, L>
67where
68 A: Author,
69 L: LogId,
70{
71 let mut remote_needs: LogRanges<A, L> = BTreeMap::default();
72
73 for (verifying_key, local_logs) in local {
75 let Some(remote_logs) = remote.get(verifying_key) else {
76 let needs = local_logs
79 .iter()
80 .map(|(log_id, log_height)| (log_id.clone(), (None, Some(*log_height))))
81 .collect();
82 remote_needs.insert(verifying_key.to_owned(), needs);
83 continue;
84 };
85
86 if local_logs == remote_logs {
88 continue;
89 }
90
91 for (log_id, local_log_height) in local_logs {
93 let Some(remote_log_height) = remote_logs.get(log_id) else {
94 remote_needs
97 .entry(verifying_key.to_owned())
98 .or_default()
99 .insert(log_id.clone(), (None, Some(*local_log_height)));
100 continue;
101 };
102
103 if remote_log_height < local_log_height {
106 remote_needs
107 .entry(verifying_key.to_owned())
108 .or_default()
109 .insert(
110 log_id.clone(),
111 (Some(*remote_log_height), Some(*local_log_height)),
112 );
113 }
114 }
115 }
116
117 remote_needs
118}
119
120#[cfg(test)]
121mod tests {
122 use std::collections::BTreeMap;
123
124 use crate::logs::compare;
125
126 type Author = u8;
127
128 impl crate::traits::Author for Author {}
129
130 const ALICE: Author = 0;
131 const BOB: Author = 1;
132
133 #[test]
134 fn both_empty() {
135 let local: BTreeMap<Author, BTreeMap<u32, u32>> = BTreeMap::new();
136 let remote: BTreeMap<Author, BTreeMap<u32, u32>> = BTreeMap::new();
137 let result = compare(&local, &remote);
138 assert!(result.is_empty());
139 }
140
141 #[test]
142 fn remote_empty() {
143 let mut local: BTreeMap<Author, BTreeMap<u32, u32>> = BTreeMap::new();
144 let logs = BTreeMap::from([(1, 5), (2, 10)]);
145 local.insert(ALICE, logs);
146
147 let remote: BTreeMap<Author, BTreeMap<u32, u32>> = BTreeMap::new();
148
149 let result = compare(&local, &remote);
150 let needs = result.get(&ALICE).unwrap();
151
152 assert_eq!(needs.get(&1), Some(&(None, Some(5))));
153 assert_eq!(needs.get(&2), Some(&(None, Some(10))));
154 }
155
156 #[test]
157 fn remote_missing_single_log() {
158 let mut local = BTreeMap::new();
159 local.insert(ALICE, BTreeMap::from([(1, 5), (2, 10)]));
160
161 let mut remote = BTreeMap::new();
162 remote.insert(ALICE, BTreeMap::from([(1, 5)]));
163
164 let result = compare(&local, &remote);
165 let needs = result.get(&ALICE).unwrap();
166
167 assert_eq!(needs.get(&2), Some(&(None, Some(10))));
168 assert!(!needs.contains_key(&1));
169 }
170
171 #[test]
172 fn remote_behind() {
173 let mut local = BTreeMap::new();
174 local.insert(ALICE, BTreeMap::from([(1, 20)]));
175
176 let mut remote = BTreeMap::new();
177 remote.insert(ALICE, BTreeMap::from([(1, 10)]));
178
179 let result = compare(&local, &remote);
180 let needs = result.get(&ALICE).unwrap();
181
182 assert_eq!(needs.get(&1), Some(&(Some(10), Some(20))));
183 }
184
185 #[test]
186 fn remote_ahead() {
187 let mut local = BTreeMap::new();
188 local.insert(ALICE, BTreeMap::from([(1, 20)]));
189
190 let mut remote = BTreeMap::new();
191 remote.insert(ALICE, BTreeMap::from([(1, 30)]));
192
193 let result = compare(&local, &remote);
194 assert!(result.is_empty());
195 }
196
197 #[test]
198 fn equal() {
199 let mut local = BTreeMap::new();
200 local.insert(ALICE, BTreeMap::from([(1, 20)]));
201
202 let mut remote = BTreeMap::new();
203 remote.insert(ALICE, BTreeMap::from([(1, 20)]));
204
205 let result = compare(&local, &remote);
206 assert!(result.is_empty());
207 }
208
209 #[test]
210 fn remote_missing_multiple_logs() {
211 let mut local = BTreeMap::new();
212 local.insert(ALICE, BTreeMap::from([(1, 5), (2, 10), (3, 15)]));
213
214 let mut remote = BTreeMap::new();
215 remote.insert(ALICE, BTreeMap::from([(1, 5)]));
216
217 let result = compare(&local, &remote);
218 let needs = result.get(&ALICE).unwrap();
219
220 assert_eq!(needs.get(&2), Some(&(None, Some(10))));
221 assert_eq!(needs.get(&3), Some(&(None, Some(15))));
222 assert!(!needs.contains_key(&1));
223 }
224
225 #[test]
226 fn remote_missing_author() {
227 let mut local = BTreeMap::new();
228 local.insert(ALICE, BTreeMap::from([(1, 5)]));
229 local.insert(BOB, BTreeMap::from([(1, 5)]));
230
231 let mut remote = BTreeMap::new();
232 remote.insert(ALICE, BTreeMap::from([(1, 5)]));
233
234 let result = compare(&local, &remote);
235 let needs = result.get(&BOB).unwrap();
236
237 assert_eq!(needs.get(&1), Some(&(None, Some(5))));
238 }
239}