summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-02-21 11:15:00 +0100
committerGitHub <noreply@github.com>2017-02-21 11:15:00 +0100
commitb67b6f53434eb551fa45b50d616f805dd39bc96b (patch)
treefe14008bf7502b540a801f691576f6a9745da360
parent2beb79581deca5c9b37cdfd01834f29c3934ecd5 (diff)
parent602bdd9762479e40101afa6c1547d577256abd7a (diff)
downloadimag-b67b6f53434eb551fa45b50d616f805dd39bc96b.zip
imag-b67b6f53434eb551fa45b50d616f805dd39bc96b.tar.gz
Merge pull request #900 from matthiasbeyer/libimagstore/doc
Libimagstore/doc
-rw-r--r--libimagstore/src/store.rs273
1 files changed, 247 insertions, 26 deletions
diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs
index d245feb..e90750a 100644
--- a/libimagstore/src/store.rs
+++ b/libimagstore/src/store.rs
@@ -190,14 +190,14 @@ impl StoreEntry {
pub struct Store {
location: PathBuf,
- /**
- * Configuration object of the store
- */
+ ///
+ /// Configuration object of the store
+ ///
configuration: Option<Value>,
- /*
- * Registered hooks
- */
+ //
+ // Registered hooks
+ //
store_unload_aspects : Arc<Mutex<Vec<Aspect>>>,
@@ -212,19 +212,42 @@ pub struct Store {
pre_move_aspects : Arc<Mutex<Vec<Aspect>>>,
post_move_aspects : Arc<Mutex<Vec<Aspect>>>,
- /**
- * Internal Path->File cache map
- *
- * Caches the files, so they remain flock()ed
- *
- * Could be optimized for a threadsafe HashMap
- */
+ ///
+ /// Internal Path->File cache map
+ ///
+ /// Caches the files, so they remain flock()ed
+ ///
+ /// Could be optimized for a threadsafe HashMap
+ ///
entries: Arc<RwLock<HashMap<StoreId, StoreEntry>>>,
}
impl Store {
/// Create a new Store object
+ ///
+ /// This opens a Store in `location` using the configuration from `store_config` (if absent, it
+ /// uses defaults).
+ ///
+ /// If the configuration is not valid, this fails.
+ ///
+ /// If the location does not exist, creating directories is by default denied and the operation
+ /// fails, if not configured otherwise.
+ /// An error is returned in this case.
+ ///
+ /// If the path exists and is a file, the operation is aborted as well, an error is returned.
+ ///
+ /// After that, the store hook aspects are created and registered in the store.
+ ///
+ /// # Return values
+ ///
+ /// - On success: Store object
+ /// - On Failure:
+ /// - ConfigurationError if config is faulty
+ /// - IoError(FileError(CreateStoreDirDenied())) if store location does not exist and creating
+ /// is denied
+ /// - StorePathCreate(_) if creating the store directory failed
+ /// - StorePathExists() if location exists but is a file
pub fn new(location: PathBuf, store_config: Option<Value>) -> Result<Store> {
use configuration::*;
@@ -399,6 +422,25 @@ impl Store {
}
/// Creates the Entry at the given location (inside the entry)
+ ///
+ /// # Executed Hooks
+ ///
+ /// - Pre create aspects
+ /// - post create aspects
+ ///
+ /// # Return value
+ ///
+ /// On success: FileLockEntry
+ ///
+ /// On error:
+ /// - Errors StoreId::into_storeid() might return
+ /// - CreateCallError(HookExecutionError(PreHookExecuteError(_)))
+ /// of the first failing pre hook.
+ /// - CreateCallError(HookExecutionError(PostHookExecuteError(_)))
+ /// of the first failing post hook.
+ /// - CreateCallError(LockPoisoned()) if the internal lock is poisened.
+ /// - CreateCallError(EntryAlreadyExists()) if the entry exists already.
+ ///
pub fn create<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> {
let id = try!(id.into_storeid()).with_base(self.path().clone());
if let Err(e) = self.execute_hooks_for_id(self.pre_create_aspects.clone(), &id) {
@@ -437,6 +479,24 @@ impl Store {
///
/// Implicitely creates a entry in the store if there is no entry with the id `id`. For a
/// non-implicitely-create look at `Store::get`.
+ ///
+ /// # Executed Hooks
+ ///
+ /// - Pre retrieve aspects
+ /// - post retrieve aspects
+ ///
+ /// # Return value
+ ///
+ /// On success: FileLockEntry
+ ///
+ /// On error:
+ /// - Errors StoreId::into_storeid() might return
+ /// - RetrieveCallError(HookExecutionError(PreHookExecuteError(_)))
+ /// of the first failing pre hook.
+ /// - RetrieveCallError(HookExecutionError(PostHookExecuteError(_)))
+ /// of the first failing post hook.
+ /// - RetrieveCallError(LockPoisoned()) if the internal lock is poisened.
+ ///
pub fn retrieve<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> {
let id = try!(id.into_storeid()).with_base(self.path().clone());
if let Err(e) = self.execute_hooks_for_id(self.pre_retrieve_aspects.clone(), &id) {
@@ -470,7 +530,19 @@ impl Store {
/// Get an entry from the store if it exists.
///
- /// This executes the {pre,post}_retrieve_aspects hooks.
+ /// # Executed Hooks
+ ///
+ /// - Pre get aspects
+ /// - post get aspects
+ ///
+ /// # Return value
+ ///
+ /// On success: Some(FileLockEntry) or None
+ ///
+ /// On error:
+ /// - Errors StoreId::into_storeid() might return
+ /// - Errors Store::retrieve() might return
+ ///
pub fn get<'a, S: IntoStoreId + Clone>(&'a self, id: S) -> Result<Option<FileLockEntry<'a>>> {
let id = try!(id.into_storeid()).with_base(self.path().clone());
@@ -490,6 +562,16 @@ impl Store {
}
/// Iterate over all StoreIds for one module name
+ ///
+ /// # Returns
+ ///
+ /// On success: An iterator over all entries in the module
+ ///
+ /// On failure:
+ /// - RetrieveForModuleCallError(GlobError(EncodingError())) if the path string cannot be
+ /// encoded
+ /// - GRetrieveForModuleCallError(GlobError(lobError())) if the glob() failed.
+ ///
pub fn retrieve_for_module(&self, mod_name: &str) -> Result<StoreIdIterator> {
let mut path = self.path().clone();
path.push(mod_name);
@@ -506,12 +588,18 @@ impl Store {
.map_err_into(SEK::RetrieveForModuleCallError)
}
- // Walk the store tree for the module
+ /// Walk the store tree for the module
+ ///
+ /// The difference between a `Walk` and a `StoreIdIterator` is that with a `Walk`, one can find
+ /// "collections" (folders).
pub fn walk<'a>(&'a self, mod_name: &str) -> Walk {
Walk::new(self.path().clone(), mod_name)
}
/// Return the `FileLockEntry` and write to disk
+ ///
+ /// See `Store::_update()`.
+ ///
pub fn update<'a>(&'a self, mut entry: FileLockEntry<'a>) -> Result<()> {
self._update(&mut entry, false).map_err_into(SEK::UpdateCallError)
}
@@ -519,8 +607,29 @@ impl Store {
/// Internal method to write to the filesystem store.
///
/// # Assumptions
+ ///
/// This method assumes that entry is dropped _right after_ the call, hence
/// it is not public.
+ ///
+ /// # Executed Hooks
+ ///
+ /// - Pre update aspects
+ /// - post update aspects
+ ///
+ /// # Return value
+ ///
+ /// On success: Entry
+ ///
+ /// On error:
+ /// - UpdateCallError(HookExecutionError(PreHookExecuteError(_)))
+ /// of the first failing pre hook.
+ /// - UpdateCallError(HookExecutionError(PostHookExecuteError(_)))
+ /// of the first failing post hook.
+ /// - UpdateCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
+ /// - IdNotFound() if the entry was not found in the stor
+ /// - Errors Entry::verify() might return
+ /// - Errors StoreEntry::write_entry() might return
+ ///
fn _update<'a>(&'a self, mut entry: &mut FileLockEntry<'a>, modify_presence: bool) -> Result<()> {
let _ = try!(self.execute_hooks_for_mut_file(self.pre_update_aspects.clone(), &mut entry)
.map_err_into(SEK::PreHookExecuteError)
@@ -546,7 +655,6 @@ impl Store {
se.status = StoreEntryStatus::Present;
}
-
self.execute_hooks_for_mut_file(self.post_update_aspects.clone(), &mut entry)
.map_err_into(SEK::PostHookExecuteError)
.map_err_into(SEK::HookExecutionError)
@@ -555,6 +663,22 @@ impl Store {
/// Retrieve a copy of a given entry, this cannot be used to mutate
/// the one on disk
+ ///
+ /// TODO: Create Hooks for retrieving a copy
+ ///
+ /// # Executed Hooks
+ ///
+ /// - (none yet)
+ ///
+ /// # Return value
+ ///
+ /// On success: Entry
+ ///
+ /// On error:
+ /// - RetrieveCopyCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
+ /// - RetrieveCopyCallError(IdLocked()) if the Entry is borrowed currently
+ /// - Errors StoreEntry::new() might return
+ ///
pub fn retrieve_copy<S: IntoStoreId>(&self, id: S) -> Result<Entry> {
let id = try!(id.into_storeid()).with_base(self.path().clone());
let entries = match self.entries.write() {
@@ -574,6 +698,25 @@ impl Store {
}
/// Delete an entry
+ ///
+ /// # Executed Hooks
+ ///
+ /// - Pre delete aspects, if the id can be used
+ /// - Post delete aspects, if the operation succeeded
+ ///
+ /// # Return value
+ ///
+ /// On success: ()
+ ///
+ /// On error:
+ /// - DeleteCallError(HookExecutionError(PreHookExecuteError(_)))
+ /// of the first failing pre hook.
+ /// - DeleteCallError(HookExecutionError(PostHookExecuteError(_)))
+ /// of the first failing post hook.
+ /// - DeleteCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
+ /// - DeleteCallError(FileNotFound()) if the StoreId refers to a non-existing entry.
+ /// - DeleteCallError(FileError()) if the internals failed to remove the file.
+ ///
pub fn delete<S: IntoStoreId>(&self, id: S) -> Result<()> {
let id = try!(id.into_storeid()).with_base(self.path().clone());
if let Err(e) = self.execute_hooks_for_id(self.pre_delete_aspects.clone(), &id) {
@@ -617,6 +760,8 @@ impl Store {
/// Save a copy of the Entry in another place
/// Executes the post_move_aspects for the new id
+ ///
+ /// TODO: Introduce new aspect for `save_to()`.
pub fn save_to(&self, entry: &FileLockEntry, new_id: StoreId) -> Result<()> {
self.save_to_other_location(entry, new_id, false)
}
@@ -624,6 +769,8 @@ impl Store {
/// Save an Entry in another place
/// Removes the original entry
/// Executes the post_move_aspects for the new id
+ ///
+ /// TODO: Introduce new aspect for `save_as()`.
pub fn save_as(&self, entry: FileLockEntry, new_id: StoreId) -> Result<()> {
self.save_to_other_location(&entry, new_id, true)
}
@@ -758,6 +905,21 @@ impl Store {
&self.location
}
+ /// Register a hook in the store.
+ ///
+ /// A hook is registered by a position (when should the hook be executed) and an aspect name.
+ /// The aspect name must be in the configuration file, so the configuration for the hook can be
+ /// passed to the `Hook` object.
+ ///
+ /// # Available Hook positions
+ ///
+ /// The hook positions are described in the type description of `HookPosition`.
+ ///
+ /// # Aspect names
+ ///
+ /// Aspect names are arbitrary, though sane things like "debug" or "vcs" are encouraged.
+ /// Refer to the documentation for more information.
+ ///
pub fn register_hook(&mut self,
position: HookPosition,
aspect_name: &str,
@@ -800,6 +962,7 @@ impl Store {
Err(SEK::HookRegisterError.into_error_with_cause(Box::new(annfe)))
}
+ /// Get the configuration for a hook by the name of the hook, from the configuration file.
fn get_config_for_hook(&self, name: &str) -> Option<&Value> {
match self.configuration {
Some(Value::Table(ref tabl)) => {
@@ -819,6 +982,13 @@ impl Store {
}
}
+ /// Execute all hooks from all aspects for a Store Id object.
+ ///
+ /// # Return value
+ ///
+ /// - () on success
+ /// - Error on the first failing hook.
+ ///
fn execute_hooks_for_id(&self,
aspects: Arc<Mutex<Vec<Aspect>>>,
id: &StoreId)
@@ -834,6 +1004,13 @@ impl Store {
.map_err(|e| HookErrorKind::HookExecutionError.into_error_with_cause(e))
}
+ /// Execute all hooks from all aspects for a mutable `FileLockEntry` object.
+ ///
+ /// # Return value
+ ///
+ /// - () on success
+ /// - Error on the first failing hook.
+ ///
fn execute_hooks_for_mut_file(&self,
aspects: Arc<Mutex<Vec<Aspect>>>,
fle: &mut FileLockEntry)
@@ -853,6 +1030,7 @@ impl Store {
impl Debug for Store {
+ /// TODO: Make pretty.
fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FMTError> {
try!(write!(fmt, " --- Store ---\n"));
try!(write!(fmt, "\n"));
@@ -877,11 +1055,12 @@ impl Debug for Store {
impl Drop for Store {
- /**
- * Unlock all files on drop
- *
- * TODO: Unlock them
- */
+ ///
+ /// Unlock all files on drop
+ //
+ /// TODO: Unlock them
+ /// TODO: Resolve this dirty hack with the StoreId for the Store drop hooks.
+ ///
fn drop(&mut self) {
match StoreId::new(Some(self.location.clone()), PathBuf::from(".")) {
Err(e) => {
@@ -909,6 +1088,10 @@ pub struct FileLockEntry<'a> {
}
impl<'a> FileLockEntry<'a, > {
+
+ /// Create a new FileLockEntry based on a `Entry` object.
+ ///
+ /// Only for internal use.
fn new(store: &'a Store, entry: Entry) -> FileLockEntry<'a> {
FileLockEntry {
store: store,
@@ -940,7 +1123,11 @@ impl<'a> DerefMut for FileLockEntry<'a> {
#[cfg(not(test))]
impl<'a> Drop for FileLockEntry<'a> {
+
/// This will silently ignore errors, use `Store::update` if you want to catch the errors
+ ///
+ /// This might panic if the store was compiled with the early-panic feature (which is not
+ /// intended for production use, though).
fn drop(&mut self) {
use libimagerror::trace::trace_error_dbg;
@@ -956,21 +1143,21 @@ impl<'a> Drop for FileLockEntry<'a> {
#[cfg(test)]
impl<'a> Drop for FileLockEntry<'a> {
+
/// This will not silently ignore errors but prints the result of the _update() call for testing
fn drop(&mut self) {
let _ = self.store._update(self, true).map_err(|e| trace_error(&e));
}
+
}
/// `EntryContent` type
pub type EntryContent = String;
-/**
- * An Entry of the store
- *
- * Contains location, header and content part.
- */
+/// An Entry of the store
+//
+/// Contains location, header and content part.
#[derive(Debug, Clone)]
pub struct Entry {
location: StoreId,
@@ -980,6 +1167,10 @@ pub struct Entry {
impl Entry {
+ /// Create a new store entry with its location at `loc`.
+ ///
+ /// This creates the entry with the default header from `Entry::default_header()` and an empty
+ /// content.
pub fn new(loc: StoreId) -> Entry {
Entry {
location: loc,
@@ -988,10 +1179,16 @@ impl Entry {
}
}
+ /// Get the default Header for an Entry.
+ ///
+ /// This function should be used to get a new Header, as the default header may change. Via
+ /// this function, compatibility is ensured.
pub fn default_header() -> Value { // BTreeMap<String, Value>
Value::default_header()
}
+ /// See `Entry::from_str()`, as this function is used internally. This is just a wrapper for
+ /// convenience.
pub fn from_reader<S: IntoStoreId>(loc: S, file: &mut Read) -> Result<Entry> {
let text = {
let mut s = String::new();
@@ -1001,6 +1198,18 @@ impl Entry {
Self::from_str(loc, &text[..])
}
+ /// Create a new Entry, with contents from the string passed.
+ ///
+ /// The passed string _must_ be a complete valid store entry, including header. So this is
+ /// probably not what end-users want to call.
+ ///
+ /// # Return value
+ ///
+ /// This errors if
+ ///
+ /// - String cannot be matched on regex to find header and content
+ /// - Header cannot be parsed into a TOML object
+ ///
pub fn from_str<S: IntoStoreId>(loc: S, s: &str) -> Result<Entry> {
debug!("Building entry from string");
lazy_static! {
@@ -1032,32 +1241,44 @@ impl Entry {
})
}
+ /// Return the string representation of this entry
+ ///
+ /// This means not only the content of the entry, but the complete entry (from memory, not from
+ /// disk).
pub fn to_str(&self) -> String {
format!("---\n{header}---\n{content}",
header = ::toml::encode_str(&self.header),
content = self.content)
}
+ /// Get the location of the Entry
pub fn get_location(&self) -> &StoreId {
&self.location
}
+ /// Get the header of the Entry
pub fn get_header(&self) -> &Value {
&self.header
}
+ /// Get the header mutably of the Entry
pub fn get_header_mut(&mut self) -> &mut Value {
&mut self.header
}
+ /// Get the content of the Entry
pub fn get_content(&self) -> &EntryContent {
&self.content
}
+ /// Get the content mutably of the Entry
pub fn get_content_mut(&mut self) -> &mut EntryContent {
&mut self.content
}
+ /// Verify the entry.
+ ///
+ /// Currently, this only verifies the header. This might change in the future.
pub fn verify(&self) -> Result<()> {
self.header.verify()
}