use std::collections::HashMap;
use libimagstore::store::Store;
use libimagstore::storeid::StoreId;
use failure::ResultExt;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use crate::linkable::*;
pub trait StoreLinkConsistentExt {
fn check_link_consistency(&self) -> Result<()>;
}
impl StoreLinkConsistentExt for Store {
fn check_link_consistency(&self) -> Result<()> {
#[derive(Debug, Default)]
struct Linking {
outgoing: Vec<StoreId>,
incoming: Vec<StoreId>,
}
let aggregate_link_network = |store: &Store| -> Result<HashMap<StoreId, Linking>> {
store
.entries()?
.into_get_iter()
.fold(Ok(HashMap::new()), |map, element| {
map.and_then(|mut map| {
debug!("Checking element = {:?}", element);
let entry = element?.ok_or_else(|| err_msg("TODO: Not yet handled"))?;
debug!("Checking entry = {:?}", entry.get_location());
let internal_links = entry
.links()?
.into_getter(store);
let mut linking = Linking::default();
for internal_link in internal_links {
debug!("internal link = {:?}", internal_link);
linking.outgoing.push(internal_link?.get_location().clone());
linking.incoming.push(entry.get_location().clone());
}
map.insert(entry.get_location().clone(), linking);
Ok(map)
})
})
};
let all_collected_storeids_exist = |network: &HashMap<StoreId, Linking>| -> Result<()> {
for (id, _) in network.iter() {
if is_match!(self.get(id.clone()), Ok(Some(_))) {
debug!("Exists in store: {:?}", id);
if !self.exists(id.clone())? {
warn!("Does exist in store but not on FS: {:?}", id);
return Err(err_msg("Link target does not exist"))
}
} else {
warn!("Does not exist in store: {:?}", id);
return Err(err_msg("Link target does not exist"))
}
}
Ok(())
};
let mk_one_directional_link_err = |src: StoreId, target: StoreId| -> Error {
format_err!("Dead link: {} -> {}",
src.local_display_string(),
target.local_display_string())
};
let incoming_links_exists_as_outgoing_links =
|src: &StoreId, linking: &Linking, network: &HashMap<StoreId, Linking>| -> Result<()> {
for link in linking.incoming.iter() {
let incoming_consistent = network.get(link)
.map(|l| l.outgoing.contains(src))
.unwrap_or(false);
if !incoming_consistent {
return Err(mk_one_directional_link_err(src.clone(), link.clone()))
}
}
Ok(())
};
let outgoing_links_exist_as_incoming_links =
|src: &StoreId, linking: &Linking, network: &HashMap<StoreId, Linking>| -> Result<()> {
for link in linking.outgoing.iter() {
let outgoing_consistent = network.get(link)
.map(|l| l.incoming.contains(src))
.unwrap_or(false);
if !outgoing_consistent {
return Err(mk_one_directional_link_err(link.clone(), src.clone()))
}
}
Ok(())
};
let nw = aggregate_link_network(&self)?;
for (k, v) in nw.iter() {
debug!("{}\n in: {:?}\n out: {:?}", k, v.incoming, v.outgoing);
}
all_collected_storeids_exist(&nw).context("Link handling error")?;
for (id, linking) in nw.iter() {
incoming_links_exists_as_outgoing_links(id, linking, &nw)?;
outgoing_links_exist_as_incoming_links(id, linking, &nw)?;
}
Ok(())
}
}