Rewrite
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:
parent
9fc9e7fe17
commit
f0969db47c
7 changed files with 110 additions and 54 deletions
|
@ -20,14 +20,16 @@ is-it-maintained-open-issues = { repository = "matthiasbeyer/imag" }
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.0"
|
log = "0.4.0"
|
||||||
toml = "0.4"
|
toml = "0.4"
|
||||||
toml-query = "0.6"
|
toml-query = "0.6"
|
||||||
is-match = "0.1"
|
|
||||||
error-chain = "0.11"
|
error-chain = "0.11"
|
||||||
|
|
||||||
libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" }
|
libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" }
|
||||||
libimagstore = { version = "0.8.0", path = "../../../lib/core/libimagstore" }
|
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]
|
[dev-dependencies]
|
||||||
env_logger = "0.5"
|
env_logger = "0.5"
|
||||||
|
|
|
@ -19,16 +19,25 @@
|
||||||
|
|
||||||
use libimagentryutil::isa::Is;
|
use libimagentryutil::isa::Is;
|
||||||
use libimagentryutil::isa::IsKindHeaderPathProvider;
|
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::CategoryError as CE;
|
||||||
|
use error::CategoryErrorKind as CEK;
|
||||||
use store::CATEGORY_REGISTER_NAME_FIELD_PATH;
|
use store::CATEGORY_REGISTER_NAME_FIELD_PATH;
|
||||||
|
use iter::CategoryEntryIterator;
|
||||||
|
|
||||||
provide_kindflag_path!(pub IsCategory, "category.is_category");
|
provide_kindflag_path!(pub IsCategory, "category.is_category");
|
||||||
|
|
||||||
pub trait Category {
|
pub trait Category {
|
||||||
fn is_category(&self) -> Result<bool>;
|
fn is_category(&self) -> Result<bool>;
|
||||||
fn get_name(&self) -> Result<String>;
|
fn get_name(&self) -> Result<String>;
|
||||||
fn get_entries(&self, store: &Store) -> Result<StoreIdIterator>;
|
fn get_entries<'a>(&self, store: &'a Store) -> Result<CategoryEntryIterator<'a>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Category for Entry {
|
impl Category for Entry {
|
||||||
|
@ -37,11 +46,19 @@ impl Category for Entry {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_name(&self) -> Result<String> {
|
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> {
|
fn get_entries<'a>(&self, store: &'a Store) -> Result<CategoryEntryIterator<'a>> {
|
||||||
unimplemented!()
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,37 +23,21 @@ use toml_query::read::TomlValueReadTypeExt;
|
||||||
use toml::Value;
|
use toml::Value;
|
||||||
|
|
||||||
use libimagstore::store::Entry;
|
use libimagstore::store::Entry;
|
||||||
|
use libimagentrylink::internal::InternalLinker;
|
||||||
|
|
||||||
use error::CategoryErrorKind as CEK;
|
use error::CategoryErrorKind as CEK;
|
||||||
use error::CategoryError as CE;
|
use error::CategoryError as CE;
|
||||||
use error::ResultExt;
|
use error::ResultExt;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use register::CategoryRegister;
|
use store::CategoryStore;
|
||||||
|
|
||||||
#[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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait EntryCategory {
|
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>;
|
fn has_category(&self) -> Result<bool>;
|
||||||
|
|
||||||
|
@ -61,9 +45,10 @@ pub trait EntryCategory {
|
||||||
|
|
||||||
impl EntryCategory for Entry {
|
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()
|
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)
|
.chain_err(|| CEK::HeaderWriteError)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
@ -71,10 +56,10 @@ impl EntryCategory for Entry {
|
||||||
/// Check whether a category exists before setting it.
|
/// Check whether a category exists before setting it.
|
||||||
///
|
///
|
||||||
/// This function should be used by default over EntryCategory::set_category()!
|
/// This function should be used by default over EntryCategory::set_category()!
|
||||||
fn set_category_checked(&mut self, register: &CategoryRegister, s: Category) -> Result<()> {
|
fn set_category_checked(&mut self, register: &CategoryStore, s: &str) -> Result<()> {
|
||||||
let c_str = s.clone().into();
|
trace!("Setting category '{}' checked", s);
|
||||||
let mut category = register
|
let mut category = register
|
||||||
.get_category_by_name(&c_str)?
|
.get_category_by_name(s)?
|
||||||
.ok_or_else(|| CE::from_kind(CEK::CategoryDoesNotExist))?;
|
.ok_or_else(|| CE::from_kind(CEK::CategoryDoesNotExist))?;
|
||||||
|
|
||||||
let _ = self.set_category(s)?;
|
let _ = self.set_category(s)?;
|
||||||
|
@ -83,15 +68,15 @@ impl EntryCategory for Entry {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_category(&self) -> Result<Option<Category>> {
|
fn get_category(&self) -> Result<String> {
|
||||||
|
trace!("Getting category from '{}'", self.get_location());
|
||||||
self.get_header()
|
self.get_header()
|
||||||
.read_string("category.value")
|
.read_string("category.value")?
|
||||||
.chain_err(|| CEK::HeaderReadError)
|
.ok_or_else(|| CE::from_kind(CEK::CategoryNameMissing))
|
||||||
.and_then(|o| o.map(Category::from).ok_or(CE::from_kind(CEK::TypeError)))
|
|
||||||
.map(Some)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_category(&self) -> Result<bool> {
|
fn has_category(&self) -> Result<bool> {
|
||||||
|
trace!("Has category? '{}'", self.get_location());
|
||||||
self.get_header().read("category.value")
|
self.get_header().read("category.value")
|
||||||
.chain_err(|| CEK::HeaderReadError)
|
.chain_err(|| CEK::HeaderReadError)
|
||||||
.map(|x| x.is_some())
|
.map(|x| x.is_some())
|
||||||
|
|
|
@ -24,6 +24,8 @@ error_chain! {
|
||||||
|
|
||||||
links {
|
links {
|
||||||
StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind);
|
StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind);
|
||||||
|
LinkError(::libimagentrylink::error::LinkError, ::libimagentrylink::error::LinkErrorKind);
|
||||||
|
EntryUtilError(::libimagentryutil::error::EntryUtilError, ::libimagentryutil::error::EntryUtilErrorKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreign_links {
|
foreign_links {
|
||||||
|
@ -65,6 +67,11 @@ error_chain! {
|
||||||
description("Type Error")
|
description("Type Error")
|
||||||
display("Type Error")
|
display("Type Error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CategoryNameMissing {
|
||||||
|
description("Category name is missing")
|
||||||
|
display("Category name is missing")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,16 @@
|
||||||
|
|
||||||
use libimagstore::storeid::StoreIdIterator;
|
use libimagstore::storeid::StoreIdIterator;
|
||||||
use libimagstore::store::Store;
|
use libimagstore::store::Store;
|
||||||
|
use libimagstore::store::FileLockEntry;
|
||||||
|
|
||||||
|
use toml_query::read::TomlValueReadTypeExt;
|
||||||
|
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use error::CategoryError as CE;
|
use error::CategoryError as CE;
|
||||||
use error::CategoryErrorKind as CEK;
|
use error::CategoryErrorKind as CEK;
|
||||||
use store::CATEGORY_REGISTER_NAME_FIELD_PATH;
|
use store::CATEGORY_REGISTER_NAME_FIELD_PATH;
|
||||||
|
use entry::EntryCategory;
|
||||||
|
use error::ResultExt;
|
||||||
|
|
||||||
/// Iterator for Category names
|
/// Iterator for Category names
|
||||||
///
|
///
|
||||||
|
@ -41,14 +46,14 @@ pub struct CategoryNameIter<'a>(&'a Store, StoreIdIterator);
|
||||||
|
|
||||||
impl<'a> CategoryNameIter<'a> {
|
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)
|
CategoryNameIter(store, sidit)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for CategoryNameIter<'a> {
|
impl<'a> Iterator for CategoryNameIter<'a> {
|
||||||
type Item = Result<Category>;
|
type Item = Result<String>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
// TODO: Optimize me with lazy_static
|
// TODO: Optimize me with lazy_static
|
||||||
|
@ -63,7 +68,6 @@ impl<'a> Iterator for CategoryNameIter<'a> {
|
||||||
.get_header()
|
.get_header()
|
||||||
.read_string(query)
|
.read_string(query)
|
||||||
.chain_err(|| CEK::HeaderReadError)?
|
.chain_err(|| CEK::HeaderReadError)?
|
||||||
.map(Category::from)
|
|
||||||
.ok_or_else(|| CE::from_kind(CEK::StoreReadError))
|
.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,14 +38,14 @@
|
||||||
extern crate toml_query;
|
extern crate toml_query;
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate is_match;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
extern crate log;
|
||||||
#[macro_use] extern crate error_chain;
|
#[macro_use] extern crate error_chain;
|
||||||
|
|
||||||
extern crate libimagerror;
|
extern crate libimagerror;
|
||||||
#[macro_use]
|
#[macro_use] extern crate libimagstore;
|
||||||
extern crate libimagstore;
|
extern crate libimagutil;
|
||||||
|
#[macro_use] extern crate libimagentryutil;
|
||||||
|
extern crate libimagentrylink;
|
||||||
|
|
||||||
pub mod category;
|
pub mod category;
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
|
|
|
@ -26,14 +26,14 @@ use toml::Value;
|
||||||
use libimagstore::store::Store;
|
use libimagstore::store::Store;
|
||||||
use libimagstore::store::FileLockEntry;
|
use libimagstore::store::FileLockEntry;
|
||||||
use libimagstore::storeid::StoreId;
|
use libimagstore::storeid::StoreId;
|
||||||
use libimagstore::storeid::StoreIdIterator;
|
use libimagentryutil::isa::Is;
|
||||||
|
|
||||||
use category::Category;
|
|
||||||
use error::CategoryErrorKind as CEK;
|
use error::CategoryErrorKind as CEK;
|
||||||
use error::CategoryError as CE;
|
use error::CategoryError as CE;
|
||||||
use error::ResultExt;
|
use error::ResultExt;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use iter::CategoryNameIter;
|
use iter::CategoryNameIter;
|
||||||
|
use category::IsCategory;
|
||||||
|
|
||||||
pub const CATEGORY_REGISTER_NAME_FIELD_PATH : &'static str = "category.register.name";
|
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 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<()>;
|
fn delete_category(&self, name: &str) -> Result<()>;
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ impl CategoryStore for Store {
|
||||||
|
|
||||||
/// Check whether a category exists
|
/// Check whether a category exists
|
||||||
fn category_exists(&self, name: &str) -> Result<bool> {
|
fn category_exists(&self, name: &str) -> Result<bool> {
|
||||||
|
trace!("Category exists? '{}'", name);
|
||||||
let sid = mk_category_storeid(self.path().clone(), name)?;
|
let sid = mk_category_storeid(self.path().clone(), name)?;
|
||||||
represents_category(self, sid, name)
|
represents_category(self, sid, name)
|
||||||
}
|
}
|
||||||
|
@ -65,7 +66,8 @@ impl CategoryStore for Store {
|
||||||
/// Create a category
|
/// Create a category
|
||||||
///
|
///
|
||||||
/// Fails if the category already exists (returns false then)
|
/// 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 sid = mk_category_storeid(self.path().clone(), name)?;
|
||||||
let mut entry = self.create(sid)?;
|
let mut entry = self.create(sid)?;
|
||||||
|
|
||||||
|
@ -75,17 +77,20 @@ impl CategoryStore for Store {
|
||||||
.get_header_mut()
|
.get_header_mut()
|
||||||
.insert(CATEGORY_REGISTER_NAME_FIELD_PATH, Value::String(String::from(name)))?;
|
.insert(CATEGORY_REGISTER_NAME_FIELD_PATH, Value::String(String::from(name)))?;
|
||||||
|
|
||||||
|
trace!("Creating category worked: '{}'", name);
|
||||||
Ok(entry)
|
Ok(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a category
|
/// Delete a category
|
||||||
fn delete_category(&self, name: &str) -> Result<()> {
|
fn delete_category(&self, name: &str) -> Result<()> {
|
||||||
|
trace!("Deleting category: '{}'", name);
|
||||||
let sid = mk_category_storeid(self.path().clone(), name)?;
|
let sid = mk_category_storeid(self.path().clone(), name)?;
|
||||||
self.delete(sid).map_err(CE::from)
|
self.delete(sid).map_err(CE::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all category names
|
/// Get all category names
|
||||||
fn all_category_names(&self) -> Result<CategoryNameIter> {
|
fn all_category_names(&self) -> Result<CategoryNameIter> {
|
||||||
|
trace!("Getting all category names");
|
||||||
Ok(CategoryNameIter::new(self, self.entries()?.without_store()))
|
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
|
/// 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).
|
/// like a normal file in the store (which is exactly what it is).
|
||||||
fn get_category_by_name(&self, name: &str) -> Result<Option<FileLockEntry>> {
|
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)?;
|
let sid = mk_category_storeid(self.path().clone(), name)?;
|
||||||
|
|
||||||
self.get(sid)
|
self.get(sid)
|
||||||
|
|
Loading…
Reference in a new issue