This patch rewrites the whole libimagcategory and brings it to a nice
standard (the code before was rather messy).

Now, categories are represented by an entry in the store and all entries
which have this category are linked to that entry.
This commit is contained in:
Matthias Beyer 2018-04-26 12:04:18 +02:00
parent 9fc9e7fe17
commit f0969db47c
7 changed files with 110 additions and 54 deletions

View file

@ -20,14 +20,16 @@ is-it-maintained-open-issues = { repository = "matthiasbeyer/imag" }
maintenance = { status = "actively-developed" }
[dependencies]
log = "0.4.0"
toml = "0.4"
toml-query = "0.6"
is-match = "0.1"
log = "0.4.0"
toml = "0.4"
toml-query = "0.6"
error-chain = "0.11"
libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" }
libimagstore = { version = "0.8.0", path = "../../../lib/core/libimagstore" }
libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" }
libimagstore = { version = "0.8.0", path = "../../../lib/core/libimagstore" }
libimagutil = { version = "0.8.0", path = "../../../lib/etc/libimagutil" }
libimagentryutil = { version = "0.8.0", path = "../../../lib/entry/libimagentryutil" }
libimagentrylink = { version = "0.8.0", path = "../../../lib/entry/libimagentrylink" }
[dev-dependencies]
env_logger = "0.5"

View file

@ -19,16 +19,25 @@
use libimagentryutil::isa::Is;
use libimagentryutil::isa::IsKindHeaderPathProvider;
use libimagstore::store::Entry;
use libimagstore::store::Store;
use libimagstore::storeid::StoreIdIterator;
use libimagentrylink::internal::InternalLinker;
use toml_query::read::TomlValueReadTypeExt;
use error::Result;
use error::CategoryError as CE;
use error::CategoryErrorKind as CEK;
use store::CATEGORY_REGISTER_NAME_FIELD_PATH;
use iter::CategoryEntryIterator;
provide_kindflag_path!(pub IsCategory, "category.is_category");
pub trait Category {
fn is_category(&self) -> Result<bool>;
fn get_name(&self) -> Result<String>;
fn get_entries(&self, store: &Store) -> Result<StoreIdIterator>;
fn is_category(&self) -> Result<bool>;
fn get_name(&self) -> Result<String>;
fn get_entries<'a>(&self, store: &'a Store) -> Result<CategoryEntryIterator<'a>>;
}
impl Category for Entry {
@ -37,11 +46,19 @@ impl Category for Entry {
}
fn get_name(&self) -> Result<String> {
self.get_header().read_string(CATEGORY_REGISTER_NAME_FIELD_PATH).map_err(CE::from)
trace!("Getting category name of '{:?}'", self.get_location());
self.get_header()
.read_string(CATEGORY_REGISTER_NAME_FIELD_PATH)
.map_err(CE::from)?
.ok_or_else(|| CE::from_kind(CEK::CategoryNameMissing))
}
fn get_entries(&self, store: &Store) -> Result<CategoryIdIterator> {
unimplemented!()
fn get_entries<'a>(&self, store: &'a Store) -> Result<CategoryEntryIterator<'a>> {
trace!("Getting linked entries for category '{:?}'", self.get_location());
let sit = self.get_internal_links()?.map(|l| l.get_store_id().clone());
let sit = StoreIdIterator::new(Box::new(sit));
let name = self.get_name()?;
Ok(CategoryEntryIterator::new(store, sit, name))
}
}

View file

@ -23,37 +23,21 @@ use toml_query::read::TomlValueReadTypeExt;
use toml::Value;
use libimagstore::store::Entry;
use libimagentrylink::internal::InternalLinker;
use error::CategoryErrorKind as CEK;
use error::CategoryError as CE;
use error::ResultExt;
use error::Result;
use register::CategoryRegister;
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Category(String);
impl From<String> for Category {
fn from(s: String) -> Category {
Category(s)
}
}
impl Into<String> for Category {
fn into(self) -> String {
self.0
}
}
use store::CategoryStore;
pub trait EntryCategory {
fn set_category(&mut self, s: Category) -> Result<()>;
fn set_category(&mut self, s: &str) -> Result<()>;
fn set_category_checked(&mut self, register: &CategoryRegister, s: Category) -> Result<()>;
fn set_category_checked(&mut self, register: &CategoryStore, s: &str) -> Result<()>;
fn get_category(&self) -> Result<Option<Category>>;
fn get_category(&self) -> Result<String>;
fn has_category(&self) -> Result<bool>;
@ -61,9 +45,10 @@ pub trait EntryCategory {
impl EntryCategory for Entry {
fn set_category(&mut self, s: Category) -> Result<()> {
fn set_category(&mut self, s: &str) -> Result<()> {
trace!("Setting category '{}' UNCHECKED", s);
self.get_header_mut()
.insert(&String::from("category.value"), Value::String(s.into()))
.insert(&String::from("category.value"), Value::String(s.to_string()))
.chain_err(|| CEK::HeaderWriteError)
.map(|_| ())
}
@ -71,10 +56,10 @@ impl EntryCategory for Entry {
/// Check whether a category exists before setting it.
///
/// This function should be used by default over EntryCategory::set_category()!
fn set_category_checked(&mut self, register: &CategoryRegister, s: Category) -> Result<()> {
let c_str = s.clone().into();
fn set_category_checked(&mut self, register: &CategoryStore, s: &str) -> Result<()> {
trace!("Setting category '{}' checked", s);
let mut category = register
.get_category_by_name(&c_str)?
.get_category_by_name(s)?
.ok_or_else(|| CE::from_kind(CEK::CategoryDoesNotExist))?;
let _ = self.set_category(s)?;
@ -83,15 +68,15 @@ impl EntryCategory for Entry {
Ok(())
}
fn get_category(&self) -> Result<Option<Category>> {
fn get_category(&self) -> Result<String> {
trace!("Getting category from '{}'", self.get_location());
self.get_header()
.read_string("category.value")
.chain_err(|| CEK::HeaderReadError)
.and_then(|o| o.map(Category::from).ok_or(CE::from_kind(CEK::TypeError)))
.map(Some)
.read_string("category.value")?
.ok_or_else(|| CE::from_kind(CEK::CategoryNameMissing))
}
fn has_category(&self) -> Result<bool> {
trace!("Has category? '{}'", self.get_location());
self.get_header().read("category.value")
.chain_err(|| CEK::HeaderReadError)
.map(|x| x.is_some())

View file

@ -24,6 +24,8 @@ error_chain! {
links {
StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind);
LinkError(::libimagentrylink::error::LinkError, ::libimagentrylink::error::LinkErrorKind);
EntryUtilError(::libimagentryutil::error::EntryUtilError, ::libimagentryutil::error::EntryUtilErrorKind);
}
foreign_links {
@ -65,6 +67,11 @@ error_chain! {
description("Type Error")
display("Type Error")
}
CategoryNameMissing {
description("Category name is missing")
display("Category name is missing")
}
}
}

View file

@ -19,11 +19,16 @@
use libimagstore::storeid::StoreIdIterator;
use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use toml_query::read::TomlValueReadTypeExt;
use error::Result;
use error::CategoryError as CE;
use error::CategoryErrorKind as CEK;
use store::CATEGORY_REGISTER_NAME_FIELD_PATH;
use entry::EntryCategory;
use error::ResultExt;
/// Iterator for Category names
///
@ -41,14 +46,14 @@ pub struct CategoryNameIter<'a>(&'a Store, StoreIdIterator);
impl<'a> CategoryNameIter<'a> {
fn new(store: &'a Store, sidit: StoreIdIterator) -> CategoryNameIter<'a> {
pub(crate) fn new(store: &'a Store, sidit: StoreIdIterator) -> CategoryNameIter<'a> {
CategoryNameIter(store, sidit)
}
}
impl<'a> Iterator for CategoryNameIter<'a> {
type Item = Result<Category>;
type Item = Result<String>;
fn next(&mut self) -> Option<Self::Item> {
// TODO: Optimize me with lazy_static
@ -63,7 +68,6 @@ impl<'a> Iterator for CategoryNameIter<'a> {
.get_header()
.read_string(query)
.chain_err(|| CEK::HeaderReadError)?
.map(Category::from)
.ok_or_else(|| CE::from_kind(CEK::StoreReadError))
};
@ -75,3 +79,38 @@ impl<'a> Iterator for CategoryNameIter<'a> {
}
}
pub struct CategoryEntryIterator<'a>(&'a Store, StoreIdIterator, String);
impl<'a> CategoryEntryIterator<'a> {
pub(crate) fn new(store: &'a Store, sit: StoreIdIterator, name: String) -> Self {
CategoryEntryIterator(store, sit, name)
}
}
impl<'a> Iterator for CategoryEntryIterator<'a> {
type Item = Result<FileLockEntry<'a>>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(next) = self.1.next() {
let getter = |next| -> Result<(String, FileLockEntry<'a>)> {
let entry = self.0
.get(next)?
.ok_or_else(|| CE::from_kind(CEK::StoreReadError))?;
Ok((entry.get_category()?, entry))
};
match getter(next) {
Err(e) => return Some(Err(e)),
Ok((c, e)) => {
if c == self.2 {
return Some(Ok(e))
// } else {
// continue
}
}
}
}
None
}
}

View file

@ -38,14 +38,14 @@
extern crate toml_query;
extern crate toml;
#[macro_use]
extern crate is_match;
#[macro_use]
extern crate log;
#[macro_use] extern crate error_chain;
extern crate libimagerror;
#[macro_use]
extern crate libimagstore;
#[macro_use] extern crate libimagstore;
extern crate libimagutil;
#[macro_use] extern crate libimagentryutil;
extern crate libimagentrylink;
pub mod category;
pub mod entry;

View file

@ -26,14 +26,14 @@ use toml::Value;
use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::StoreIdIterator;
use libimagentryutil::isa::Is;
use category::Category;
use error::CategoryErrorKind as CEK;
use error::CategoryError as CE;
use error::ResultExt;
use error::Result;
use iter::CategoryNameIter;
use category::IsCategory;
pub const CATEGORY_REGISTER_NAME_FIELD_PATH : &'static str = "category.register.name";
@ -44,7 +44,7 @@ pub trait CategoryStore {
fn category_exists(&self, name: &str) -> Result<bool>;
fn create_category(&self, name: &str) -> Result<bool>;
fn create_category<'a>(&'a self, name: &str) -> Result<FileLockEntry<'a>>;
fn delete_category(&self, name: &str) -> Result<()>;
@ -58,6 +58,7 @@ impl CategoryStore for Store {
/// Check whether a category exists
fn category_exists(&self, name: &str) -> Result<bool> {
trace!("Category exists? '{}'", name);
let sid = mk_category_storeid(self.path().clone(), name)?;
represents_category(self, sid, name)
}
@ -65,7 +66,8 @@ impl CategoryStore for Store {
/// Create a category
///
/// Fails if the category already exists (returns false then)
fn create_category(&self, name: &str) -> Result<FileLockEntry<'a>> {
fn create_category<'a>(&'a self, name: &str) -> Result<FileLockEntry<'a>> {
trace!("Creating category: '{}'", name);
let sid = mk_category_storeid(self.path().clone(), name)?;
let mut entry = self.create(sid)?;
@ -75,17 +77,20 @@ impl CategoryStore for Store {
.get_header_mut()
.insert(CATEGORY_REGISTER_NAME_FIELD_PATH, Value::String(String::from(name)))?;
trace!("Creating category worked: '{}'", name);
Ok(entry)
}
/// Delete a category
fn delete_category(&self, name: &str) -> Result<()> {
trace!("Deleting category: '{}'", name);
let sid = mk_category_storeid(self.path().clone(), name)?;
self.delete(sid).map_err(CE::from)
}
/// Get all category names
fn all_category_names(&self) -> Result<CategoryNameIter> {
trace!("Getting all category names");
Ok(CategoryNameIter::new(self, self.entries()?.without_store()))
}
@ -94,6 +99,7 @@ impl CategoryStore for Store {
/// Returns the FileLockEntry which represents the category, so one can link to it and use it
/// like a normal file in the store (which is exactly what it is).
fn get_category_by_name(&self, name: &str) -> Result<Option<FileLockEntry>> {
trace!("Getting category by name: '{}'", name);
let sid = mk_category_storeid(self.path().clone(), name)?;
self.get(sid)