summaryrefslogtreecommitdiffstats
path: root/lib/entry/libimagentryref/src/refstore.rs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/entry/libimagentryref/src/refstore.rs')
-rw-r--r--lib/entry/libimagentryref/src/refstore.rs313
1 files changed, 70 insertions, 243 deletions
diff --git a/lib/entry/libimagentryref/src/refstore.rs b/lib/entry/libimagentryref/src/refstore.rs
index 7248bbe8..5f1c780d 100644
--- a/lib/entry/libimagentryref/src/refstore.rs
+++ b/lib/entry/libimagentryref/src/refstore.rs
@@ -17,280 +17,107 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
+use std::path::Path;
use std::path::PathBuf;
-use std::collections::BTreeMap;
-use std::fs::File;
use libimagstore::store::FileLockEntry;
-use libimagstore::storeid::IntoStoreId;
-use libimagstore::storeid::StoreId;
-use libimagstore::storeid::StoreIdIterator;
use libimagstore::store::Store;
+use libimagstore::storeid::StoreId;
use libimagentryutil::isa::Is;
+use toml_query::insert::TomlValueInsertExt;
use toml::Value;
-use error::RefErrorKind as REK;
use error::RefError as RE;
-use error::ResultExt;
-use error::Result;
-use flags::RefFlags;
+use error::RefErrorKind as REK;
use reference::IsRef;
-use hasher::*;
-use module_path::ModuleEntryPath;
-use util::*;
-
-pub trait RefStore {
-
- /// Check whether there is a reference to the file at `pb`
- fn exists(&self, pb: PathBuf) -> Result<bool>;
-
- /// Get a Ref object from the store by hash.
- ///
- /// Returns None if the hash cannot be found.
- fn get_by_hash<'a>(&'a self, hash: String) -> Result<Option<FileLockEntry<'a>>>;
-
- /// Find a store id by partial ref (also see documentation for
- /// `RefStore::get_by_partitial_hash()`.
- fn find_storeid_by_partial_hash(&self, hash: &String) -> Result<Option<StoreId>>;
-
- /// Get a Ref object from the store by (eventually partial) hash.
- ///
- /// If the hash is complete, `RefStore::get_by_hash()` should be used as it is cheaper.
- /// If the hash comes from user input and thus might be abbreviated, this function can be used.
- fn get_by_partitial_hash<'a>(&'a self, hash: &String) -> Result<Option<FileLockEntry<'a>>>;
- /// Delete a ref by hash
- ///
- /// If the returned Result contains an error, the ref might not be deleted.
- fn delete_by_hash(&self, hash: String) -> Result<()>;
+/// A UniqueRefPathGenerator generates unique Pathes
+///
+/// It is basically a functor which generates a StoreId from a &Path.
+/// For more information have a look at the documentation of RefStore.
+pub trait UniqueRefPathGenerator {
+ type Error: From<RE>;
- /// Create a Ref object which refers to `pb`
- fn create<'a>(&'a self, pb: PathBuf, flags: RefFlags) -> Result<FileLockEntry<'a>>;
-
- fn create_with_hasher<'a, H: Hasher>(&'a self, pb: PathBuf, flags: RefFlags, h: H)
- -> Result<FileLockEntry<'a>>;
+ /// The collection the `StoreId` should be created for
+ fn collection() -> &'static str {
+ "ref"
+ }
- /// Get all reference objects
- fn all_references(&self) -> Result<StoreIdIterator>;
+ /// A function which should generate a unique string for a Path
+ fn unique_hash<A: AsRef<Path>>(path: A) -> Result<String, Self::Error>;
+ /// Postprocess the generated `StoreId` object
+ fn postprocess_storeid(sid: StoreId) -> Result<StoreId, Self::Error> {
+ Ok(sid)
+ }
}
-impl RefStore for Store {
-
- /// Check whether there is a reference to the file at `pb`
- fn exists(&self, pb: PathBuf) -> Result<bool> {
- pb.canonicalize()
- .chain_err(|| REK::PathCanonicalizationError)
- .and_then(|c| hash_path(&c))
- .chain_err(|| REK::PathHashingError)
- .and_then(|hash| {
- self.retrieve_for_module("ref").map(|iter| (hash, iter)).map_err(From::from)
- })
- .and_then(|(hash, possible_refs)| {
- // This is kind of a manual Iterator::filter() call what we do here, but with the
- // actual ::filter method we cannot return the error in a nice way, so we do it
- // manually here. If you can come up with a better version of this, feel free to
- // take this note as a todo.
- for r in possible_refs {
- let contains_hash = r.to_str()
- .chain_err(|| REK::TypeConversionError)
- .map(|s| s.contains(&hash[..]))
- ?;
+/// A extensions for the `Store` to handle `Ref` objects
+///
+/// The RefStore handles refs using a `UniqueRefPathGenerator`. The `UniqueRefPathGenerator`, as it
+/// name suggests, generates unique `StoreId`s for a `&Path`. It is a functor `&Path -> StoreId`.
+///
+/// It provides three functions which are called in the following sequence:
+///
+/// * The `UniqueRefPathGenerator::collection()` function is used for get the collection a `StoreId`
+/// should be in (The first element of the `StoreId` path)
+/// * The `UniqueRefPathGenerator::unique_hash()` gets the `&Path` which it then should generate a
+/// unique String for. How this is done does not matter. It can hash the Path itself, read the
+/// file and hash that or something else. It should be reproduceable, though.
+/// * These two parts are joined and put into a `StoreId` which the
+/// `UniqueRefPathGenerator::postprocess_storeid()` function is then allowed to postprocess (for
+/// example add more parts to the StoreId). The default implementation does nothing.
+///
+/// The StoreId which is generated is then used to carry out the actual action (reading, creating
+/// ...).
+/// If a entry is created, header information is set (that it is a ref, the hash which was just
+/// generated and the path of the referenced file)
+///
+/// # Details
+///
+/// The `UniqueRefPathGenerator` is passed as type parameter to enforce some semantics:
+///
+/// * The used `UniqueRefPathGenerator` is defined by the implementation rather than by the runtime
+/// of the program or some environment. Of course this is only a small hurdle to enforce this, but
+/// a hint.
+/// * The `UniqueRefPathGenerator` is a functor which does not carry state.
+///
+pub trait RefStore<'a> {
+
+ fn get_ref<RPG: UniqueRefPathGenerator>(&'a self, hash: &String) -> Result<Option<FileLockEntry<'a>>, RPG::Error>;
+ fn create_ref<RPG: UniqueRefPathGenerator, A: AsRef<Path>>(&'a self, path: A) -> Result<FileLockEntry<'a>, RPG::Error>;
+ fn retrieve_ref<RPG: UniqueRefPathGenerator, A: AsRef<Path>>(&'a self, path: A) -> Result<FileLockEntry<'a>, RPG::Error>;
- if !contains_hash {
- continue;
- }
-
- match self.get(r.clone())? {
- Some(fle) => {
- if read_reference(&fle).map(|path| path == pb).unwrap_or(false) {
- return Ok(true)
- }
- },
-
- None => {
- let e = format!("Failed to get from store: {}", r);
- return Err(e).map_err(From::from)
- },
- }
- }
+}
- Ok(false)
- })
- }
+impl<'a> RefStore<'a> for Store {
- /// Get a Ref object from the store by hash.
- ///
- /// Returns None if the hash cannot be found.
- fn get_by_hash<'a>(&'a self, hash: String) -> Result<Option<FileLockEntry<'a>>> {
- ModuleEntryPath::new(hash)
- .into_storeid()
- .and_then(|id| self.get(id))
- .map_err(From::from)
+ fn get_ref<RPG: UniqueRefPathGenerator>(&self) -> Result<Option<FileLockEntry<'a>>, RPG::Error> {
+ unimplemented!()
}
- fn find_storeid_by_partial_hash(&self, hash: &String) -> Result<Option<StoreId>> {
- debug!("Trying to find '{}' in store...", hash);
- for id in self.retrieve_for_module("ref")? {
- let components_have_hash = id
- .components()
- .any(|c| c.as_os_str().to_str().map(|s| s.contains(hash)).unwrap_or(false));
-
- if components_have_hash {
- debug!("Found hash '{}' in {:?}", hash, id);
- return Ok(Some(id))
- }
- }
- Ok(None)
+ fn create_ref<RPG: UniqueRefPathGenerator>(&self) -> Result<FileLockEntry<'a>, RPG::Error> {
+ unimplemented!()
}
- /// Get a Ref object from the store by (eventually partial) hash.
- ///
- /// If the hash is complete, `RefStore::get_by_hash()` should be used as it is cheaper.
- /// If the hash comes from user input and thus might be abbreviated, this function can be used.
- fn get_by_partitial_hash<'a>(&'a self, hash: &String) -> Result<Option<FileLockEntry<'a>>> {
- match self.find_storeid_by_partial_hash(hash)? {
- Some(id) => self.get(id).map_err(From::from),
- None => Ok(None),
- }
+ fn retrieve_ref<RPG: UniqueRefPathGenerator>(&self) Result<FileLockEntry<'a>, RPG::Error> {
+ unimplemented!()
}
- /// Delete a ref by hash
- ///
- /// If the returned Result contains an error, the ref might not be deleted.
- fn delete_by_hash(&self, hash: String) -> Result<()> {
- ModuleEntryPath::new(hash)
- .into_storeid()
- .and_then(|id| self.delete(id))
- .map_err(From::from)
+ fn delete_ref<RPG: UniqueRefPathGenerator>(&self) -> Result<(), RPG::Error> {
+ unimplemented!()
}
- /// Create a Ref object which refers to `pb`
- fn create<'a>(&'a self, pb: PathBuf, flags: RefFlags) -> Result<FileLockEntry<'a>> {
- self.create_with_hasher(pb, flags, DefaultHasher::new())
+ fn ref_exists<RPG: UniqueRefPathGenerator>(&self) -> Result<bool, RPG::Error> {
+ unimplemented!()
}
- fn create_with_hasher<'a, H: Hasher>(&'a self, pb: PathBuf, flags: RefFlags, mut h: H)
- -> Result<FileLockEntry<'a>>
+ fn move_ref_by_id<OLDRPG: UniqueRefPathGenerator, NEWRPG: UniqueRefPathGenerator>(&self)
+ -> Result<(), Either<OLDRPG::Error, NEWRPG::Error>>
{
- use toml_query::insert::TomlValueInsertExt;
-
- if !pb.exists() {
- return Err(RE::from_kind(REK::RefTargetDoesNotExist));
- }
- if flags.get_content_hashing() && pb.is_dir() {
- return Err(RE::from_kind(REK::RefTargetCannotBeHashed));
- }
-
- let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold
- File::open(pb.clone())
- .chain_err(|| REK::RefTargetFileCannotBeOpened)
-
- // If we were able to open this file,
- // we hash the contents of the file and return (file, hash)
- .and_then(|mut file| {
- let opt_contenthash = if flags.get_content_hashing() {
- Some(h.create_hash(&pb, &mut file)?)
- } else {
- None
- };
-
- Ok((file, opt_contenthash))
- })
-
- // and then we get the permissions if we have to
- // and return (file, content hash, permissions)
- .and_then(|(file, opt_contenthash)| {
- let opt_permissions = if flags.get_permission_tracking() {
- Some(file.metadata()
- .map(|md| md.permissions())
- .chain_err(|| REK::RefTargetCannotReadPermissions)?)
- } else {
- None
- };
-
- Ok((opt_contenthash, opt_permissions))
- })
-
- // and then we try to canonicalize the PathBuf, because we want to store a
- // canonicalized path
- // and return (file, content hash, permissions, canonicalized path)
- .and_then(|(opt_contenthash, opt_permissions)| {
- pb.canonicalize()
- .map(|can| (opt_contenthash, opt_permissions, can))
- // if PathBuf::canonicalize() failed, build an error from the return value
- .chain_err(|| REK::PathCanonicalizationError)
- })
-
- // and then we hash the canonicalized path
- // and return (file, content hash, permissions, canonicalized path, path hash)
- .and_then(|(opt_contenthash, opt_permissions, can)| {
- let path_hash = hash_path(&can).chain_err(|| REK::PathHashingError)?;
-
- Ok((opt_contenthash, opt_permissions, can, path_hash))
- })
-
- // and then we convert the PathBuf of the canonicalized path to a String to be able
- // to save it in the Ref FileLockEntry obj
- // and return
- // (file, content hash, permissions, canonicalized path as String, path hash)
- .and_then(|(opt_conhash, opt_perm, can, path_hash)| {
- match can.to_str().map(String::from) {
- // UTF convert error in PathBuf::to_str(),
- None => Err(RE::from_kind(REK::PathUTF8Error)),
- Some(can) => Ok((opt_conhash, opt_perm, can, path_hash))
- }
- })
-
- // and then we create the FileLockEntry in the Store
- // and return (filelockentry, content hash, permissions, canonicalized path)
- .and_then(|(opt_conhash, opt_perm, can, path_hash)| {
- let fle = self.create(ModuleEntryPath::new(path_hash))?;
- Ok((fle, opt_conhash, opt_perm, can))
- })?
- };
-
- for tpl in [
- Some((String::from("ref"), Value::Table(BTreeMap::new()))),
- Some((String::from("ref.permissions"), Value::Table(BTreeMap::new()))),
- Some((String::from("ref.path"), Value::String(canonical_path))),
- Some((String::from("ref.content_hash"), Value::Table(BTreeMap::new()))),
-
- content_hash.map(|hash| {
- (format!("ref.content_hash.{}", h.hash_name()), Value::String(hash))
- }),
- permissions.map(|p| {
- (String::from("ref.permissions.ro"), Value::Boolean(p.readonly()))
- }),
- ].into_iter()
- {
- match tpl {
- &Some((ref s, ref v)) => {
- match fle.get_header_mut().insert(s, v.clone()) {
- Ok(Some(_)) => {
- let e = RE::from_kind(REK::HeaderFieldAlreadyExistsError);
- return Err(e).chain_err(|| REK::HeaderFieldWriteError);
- },
- Ok(None) => {
- // Okay, we just inserted a new header value...
- },
- Err(e) => return Err(e).chain_err(|| REK::HeaderFieldWriteError),
- }
- }
- &None => {
- debug!("Not going to insert.");
- }
- }
- }
-
- let _ = fle.set_isflag::<IsRef>()?;
-
- Ok(fle)
+ unimplemented!()
}
- fn all_references(&self) -> Result<StoreIdIterator> {
- self.retrieve_for_module("ref").map_err(From::from)
- }
}