use std::collections::{HashMap, HashSet, VecDeque};
use crate::{
Result,
backend::BackendImpl,
entry::{Entry, ID},
sync::error::SyncError,
};
pub fn collect_missing_ancestors(backend: &dyn BackendImpl, entry_ids: &[ID]) -> Result<Vec<ID>> {
let mut missing = Vec::new();
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
for id in entry_ids {
queue.push_back(id.clone());
}
while let Some(entry_id) = queue.pop_front() {
if visited.contains(&entry_id) {
continue;
}
visited.insert(entry_id.clone());
match backend.get(&entry_id) {
Ok(entry) => {
if let Ok(parents) = entry.parents() {
for parent_id in parents {
if !visited.contains(&parent_id) {
queue.push_back(parent_id);
}
}
}
}
Err(e) if e.is_not_found() => {
missing.push(entry_id);
}
Err(e) => {
return Err(SyncError::BackendError(format!(
"Failed to check for entry {entry_id}: {e}"
))
.into());
}
}
}
Ok(missing)
}
pub fn collect_ancestors_to_send(
backend: &dyn BackendImpl,
entry_ids: &[ID],
their_tips: &[ID],
) -> Result<Vec<Entry>> {
let mut entries_to_send = HashMap::new();
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
let their_tips_set: HashSet<&ID> = their_tips.iter().collect();
for id in entry_ids {
queue.push_back(id.clone());
}
while let Some(entry_id) = queue.pop_front() {
if visited.contains(&entry_id) || their_tips_set.contains(&entry_id) {
continue; }
visited.insert(entry_id.clone());
match backend.get(&entry_id) {
Ok(entry) => {
entries_to_send.insert(entry_id.clone(), entry.clone());
if let Ok(parents) = entry.parents() {
for parent_id in parents {
if !their_tips_set.contains(&parent_id) && !visited.contains(&parent_id) {
queue.push_back(parent_id);
}
}
}
}
Err(e) => {
return Err(SyncError::BackendError(format!(
"Failed to get entry {entry_id} to send: {e}"
))
.into());
}
}
}
let entries: Vec<Entry> = entries_to_send.into_values().collect();
Ok(entries)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use crate::{Entry, backend::database::InMemory};
fn create_test_backend() -> Arc<InMemory> {
Arc::new(InMemory::new())
}
#[test]
fn test_collect_missing_ancestors_empty() {
let backend = create_test_backend();
let result = collect_missing_ancestors(backend.as_ref(), &[]).unwrap();
assert!(result.is_empty());
}
#[test]
fn test_collect_missing_ancestors_not_found() {
let backend = create_test_backend();
let missing_id = ID::from("missing123");
let result =
collect_missing_ancestors(backend.as_ref(), std::slice::from_ref(&missing_id)).unwrap();
assert_eq!(result, vec![missing_id]);
}
#[test]
fn test_collect_missing_ancestors_present() {
let backend = create_test_backend();
let entry = Entry::root_builder()
.build()
.expect("Root entry should build successfully");
let entry_id = entry.id();
backend.put_verified(entry).unwrap();
let result = collect_missing_ancestors(backend.as_ref(), &[entry_id]).unwrap();
assert!(result.is_empty()); }
#[test]
fn test_collect_ancestors_to_send_empty() {
let backend = create_test_backend();
let result = collect_ancestors_to_send(backend.as_ref(), &[], &[]).unwrap();
assert!(result.is_empty());
}
#[test]
fn test_collect_ancestors_to_send_single_entry() {
let backend = create_test_backend();
let entry = Entry::root_builder()
.build()
.expect("Root entry should build successfully");
let entry_id = entry.id();
backend.put_verified(entry.clone()).unwrap();
let result = collect_ancestors_to_send(backend.as_ref(), &[entry_id], &[]).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].id(), entry.id());
}
#[test]
fn test_collect_ancestors_to_send_peer_already_has() {
let backend = create_test_backend();
let entry = Entry::root_builder()
.build()
.expect("Root entry should build successfully");
let entry_id = entry.id();
backend.put_verified(entry).unwrap();
let result = collect_ancestors_to_send(
backend.as_ref(),
std::slice::from_ref(&entry_id),
std::slice::from_ref(&entry_id),
)
.unwrap();
assert!(result.is_empty());
}
}