Impl StoreLinkConsistentExt::check_link_consistency()

This commit is contained in:
Matthias Beyer 2017-06-06 15:15:08 +02:00
parent 83647e52a0
commit 13b24cb397

View file

@ -586,6 +586,7 @@ fn process_rw_result(links: StoreResult<Option<Value>>) -> Result<LinkIter> {
}
pub mod store_check {
use libimagstore::store::Store;
pub mod error {
generate_error_imports!();
@ -610,7 +611,9 @@ pub mod store_check {
StoreLinkConsistencyError,
StoreLinkConsistencyErrorKind,
StoreLinkConsistencyErrorCustomData,
StoreLinkConsistencyError => "Links in the store are not consistent"
StoreLinkConsistencyError => "Links in the store are not consistent",
LinkHandlingError => "Error in link handling",
StoreError => "Error while talking to the store"
);
generate_result_helper!(StoreLinkConsistencyError, StoreLinkConsistencyErrorKind);
@ -629,8 +632,194 @@ pub mod store_check {
}
pub trait StoreLinkConsistentExt {
fn check_link_consistency(&self) -> Result<()>;
}
impl StoreLinkConsistentExt for Store {
fn check_link_consistency(&self) -> Result<()> {
unimplemented!()
use std::collections::HashMap;
use self::error::StoreLinkConsistencyErrorKind as SLCEK;
use self::error::StoreLinkConsistencyError as SLCE;
use self::error::StoreLinkConsistencyErrorCustomData as SLCECD;
use error::LinkErrorKind as LEK;
use result::Result as LResult;
use internal::InternalLinker;
use libimagstore::store::StoreObject;
use libimagstore::storeid::StoreId;
use libimagerror::iter::TraceIterator;
use libimagerror::into::IntoError;
use libimagutil::iter::FoldResult;
// Helper data structure to collect incoming and outgoing links for each StoreId
#[derive(Debug, Default)]
struct Linking {
outgoing: Vec<StoreId>,
incoming: Vec<StoreId>,
}
/// Helper function to aggregate the Link network
///
/// This function aggregates a HashMap which maps each StoreId object in the store onto
/// a Linking object, which contains a list of StoreIds which this entry links to and a
/// list of StoreIds which link to the current one.
///
/// The lambda returns an error if something fails
let aggregate_link_network = |store: &Store| -> Result<HashMap<StoreId, Linking>> {
store
.walk("") // this is a hack... I know...
.filter_map(|obj: StoreObject| match obj {
StoreObject::Id(id) => Some(id),
_ => None
}) // Only ids are interesting
.fold(Ok(HashMap::new()), |acc, sid| {
acc.and_then(|mut state| {
debug!("Checking entry: '{}'", sid);
match try!(self.get(sid).map_err_into(SLCEK::StoreError)) {
Some(fle) => {
debug!("Found FileLockEntry");
let fle_loc = fle.get_location();
let internal_links = fle
.get_internal_links()
.map_err_into(SLCEK::StoreError)?
.into_getter(self) // get the FLEs from the Store
.trace_unwrap(); // trace all Err(e)s and get the Ok(fle)s
for internal_link in internal_links {
let il_loc = internal_link.get_location();
state
.entry(il_loc.clone())
.or_insert(Linking::default())
.incoming
.push(fle_loc.clone());
// Make sure an empty linking object is present for the
// current StoreId object
state
.entry(fle_loc.clone())
.or_insert(Linking::default())
.outgoing
.push(il_loc.clone());
}
Ok(state)
},
None => {
debug!("No entry");
Ok(state)
}
}
})
})
};
/// Helper to check whethre all StoreIds in the network actually exists
///
/// Because why not?
let all_collected_storeids_exist = |network: &HashMap<StoreId, Linking>| -> LResult<()> {
network
.iter()
.fold_result(|(id, _)| {
if is_match!(self.get(id.clone()), Ok(Some(_))) {
debug!("Exists in store: {:?}", id);
let exists = {
use error::MapErrInto as MEI;
try!(MEI::map_err_into(id.exists(), LEK::StoreReadError))
};
if !exists {
warn!("Does exist in store but not on FS: {:?}", id);
Err(LEK::LinkTargetDoesNotExist.into_error())
} else {
Ok(())
}
} else {
warn!("Does not exist in store: {:?}", id);
Err(LEK::LinkTargetDoesNotExist.into_error())
}
})
};
/// Helper function to create a SLCECD::OneDirectionalLink error object
#[inline]
let mk_one_directional_link_err = |src: StoreId, target: StoreId| -> SLCE {
// construct the error
let custom = SLCECD::OneDirectionalLink {
source: src,
target: target,
};
SLCEK::StoreLinkConsistencyError
.into_error()
.with_custom_data(custom)
};
/// Helper lambda to check whether the _incoming_ links of each entry actually also
/// appear in the _outgoing_ list of the linked entry
let incoming_links_exists_as_outgoing_links =
|src: &StoreId, linking: &Linking, network: &HashMap<StoreId, Linking>| -> Result<()> {
linking
.incoming
.iter()
.fold_result(|link| {
// Check whether the links which are _incoming_ on _src_ are outgoing
// in each of the links in the incoming list.
let incoming_consistent = network.get(link)
.map(|l| l.outgoing.contains(src))
.unwrap_or(false);
if !incoming_consistent {
Err(mk_one_directional_link_err(src.clone(), link.clone()))
} else {
Ok(())
}
})
};
/// Helper lambda to check whether the _outgoing links of each entry actually also
/// appear in the _incoming_ list of the linked entry
let outgoing_links_exist_as_incoming_links =
|src: &StoreId, linking: &Linking, network: &HashMap<StoreId, Linking>| -> Result<()> {
linking
.outgoing
.iter()
.fold_result(|link| {
// Check whether the links which are _outgoing_ on _src_ are incoming
// in each of the links in the outgoing list.
let outgoing_consistent = network.get(link)
.map(|l| l.incoming.contains(src))
.unwrap_or(false);
if !outgoing_consistent {
Err(mk_one_directional_link_err(link.clone(), src.clone()))
} else {
Ok(())
}
})
};
aggregate_link_network(&self)
.and_then(|nw| {
all_collected_storeids_exist(&nw)
.map(|_| nw)
.map_err_into(SLCEK::LinkHandlingError)
})
.and_then(|nw| {
nw.iter().fold_result(|(id, linking)| {
try!(incoming_links_exists_as_outgoing_links(id, linking, &nw));
try!(outgoing_links_exist_as_incoming_links(id, linking, &nw));
Ok(())
})
})
.map(|_| ())
}
}