Impl StoreLinkConsistentExt::check_link_consistency()
This commit is contained in:
parent
83647e52a0
commit
13b24cb397
1 changed files with 191 additions and 2 deletions
|
@ -586,6 +586,7 @@ fn process_rw_result(links: StoreResult<Option<Value>>) -> Result<LinkIter> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod store_check {
|
pub mod store_check {
|
||||||
|
use libimagstore::store::Store;
|
||||||
|
|
||||||
pub mod error {
|
pub mod error {
|
||||||
generate_error_imports!();
|
generate_error_imports!();
|
||||||
|
@ -610,7 +611,9 @@ pub mod store_check {
|
||||||
StoreLinkConsistencyError,
|
StoreLinkConsistencyError,
|
||||||
StoreLinkConsistencyErrorKind,
|
StoreLinkConsistencyErrorKind,
|
||||||
StoreLinkConsistencyErrorCustomData,
|
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);
|
generate_result_helper!(StoreLinkConsistencyError, StoreLinkConsistencyErrorKind);
|
||||||
|
@ -629,8 +632,194 @@ pub mod store_check {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait StoreLinkConsistentExt {
|
pub trait StoreLinkConsistentExt {
|
||||||
|
fn check_link_consistency(&self) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StoreLinkConsistentExt for Store {
|
||||||
fn check_link_consistency(&self) -> Result<()> {
|
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(|_| ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue