summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2018-04-27 14:18:38 +0200
committerGitHub <noreply@github.com>2018-04-27 14:18:38 +0200
commitead8413c1204d8c509123d5d8e6270c3df441e04 (patch)
tree9765fd0f9fe3a49100f3e1886e5ebb21f8bc9c63
parent493fa0226cc3bfec392a2d5309142e45a47726b2 (diff)
parent82867f3ff6b95d0372e586cc5b36861f7679c54c (diff)
downloadimag-ead8413c1204d8c509123d5d8e6270c3df441e04.zip
imag-ead8413c1204d8c509123d5d8e6270c3df441e04.tar.gz
Merge pull request #1451 from matthiasbeyer/libimagcategory/rewrite-with-linking
libimagcategory: rewrite with linking
-rw-r--r--lib/entry/libimagentrycategory/Cargo.toml14
-rw-r--r--lib/entry/libimagentrycategory/src/category.rs95
-rw-r--r--lib/entry/libimagentrycategory/src/entry.rs85
-rw-r--r--lib/entry/libimagentrycategory/src/error.rs7
-rw-r--r--lib/entry/libimagentrycategory/src/iter.rs116
-rw-r--r--lib/entry/libimagentrycategory/src/lib.rs12
-rw-r--r--lib/entry/libimagentrycategory/src/store.rs (renamed from lib/entry/libimagentrycategory/src/register.rs)120
7 files changed, 283 insertions, 166 deletions
diff --git a/lib/entry/libimagentrycategory/Cargo.toml b/lib/entry/libimagentrycategory/Cargo.toml
index 472d1b1..70c6ee0 100644
--- a/lib/entry/libimagentrycategory/Cargo.toml
+++ b/lib/entry/libimagentrycategory/Cargo.toml
@@ -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"
diff --git a/lib/entry/libimagentrycategory/src/category.rs b/lib/entry/libimagentrycategory/src/category.rs
index cd0e09a..fab8715 100644
--- a/lib/entry/libimagentrycategory/src/category.rs
+++ b/lib/entry/libimagentrycategory/src/category.rs
@@ -17,81 +17,48 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
-use toml_query::insert::TomlValueInsertExt;
-use toml_query::read::TomlValueReadExt;
-use toml_query::read::TomlValueReadTypeExt;
-use toml::Value;
-
+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 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
- }
-}
-
-pub trait EntryCategory {
-
- fn set_category(&mut self, s: Category) -> Result<()>;
-
- fn set_category_checked(&mut self, register: &CategoryRegister, s: Category) -> Result<()>;
+use toml_query::read::TomlValueReadTypeExt;
- fn get_category(&self) -> Result<Option<Category>>;
+use error::Result;
+use error::CategoryError as CE;
+use error::CategoryErrorKind as CEK;
+use store::CATEGORY_REGISTER_NAME_FIELD_PATH;
+use iter::CategoryEntryIterator;
- fn has_category(&self) -> Result<bool>;
+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<'a>(&self, store: &'a Store) -> Result<CategoryEntryIterator<'a>>;
}
-impl EntryCategory for Entry {
-
- fn set_category(&mut self, s: Category) -> Result<()> {
- self.get_header_mut()
- .insert(&String::from("category.value"), Value::String(s.into()))
- .chain_err(|| CEK::HeaderWriteError)
- .map(|_| ())
- }
-
- /// 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<()> {
- register.category_exists(&s.0)
- .and_then(|bl| if bl {
- self.set_category(s)
- } else {
- Err(CE::from_kind(CEK::CategoryDoesNotExist))
- })
+impl Category for Entry {
+ fn is_category(&self) -> Result<bool> {
+ self.is::<IsCategory>().map_err(CE::from)
}
- fn get_category(&self) -> Result<Option<Category>> {
+ fn get_name(&self) -> Result<String> {
+ trace!("Getting category name of '{:?}'", 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_REGISTER_NAME_FIELD_PATH)
+ .map_err(CE::from)?
+ .ok_or_else(|| CE::from_kind(CEK::CategoryNameMissing))
}
- fn has_category(&self) -> Result<bool> {
- self.get_header().read("category.value")
- .chain_err(|| CEK::HeaderReadError)
- .map(|x| x.is_some())
+ 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))
}
-
}
+
diff --git a/lib/entry/libimagentrycategory/src/entry.rs b/lib/entry/libimagentrycategory/src/entry.rs
new file mode 100644
index 0000000..c6f82e0
--- /dev/null
+++ b/lib/entry/libimagentrycategory/src/entry.rs
@@ -0,0 +1,85 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; version
+// 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+use toml_query::insert::TomlValueInsertExt;
+use toml_query::read::TomlValueReadExt;
+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 store::CategoryStore;
+
+pub trait EntryCategory {
+
+ fn set_category(&mut self, s: &str) -> Result<()>;
+
+ fn set_category_checked(&mut self, register: &CategoryStore, s: &str) -> Result<()>;
+
+ fn get_category(&self) -> Result<String>;
+
+ fn has_category(&self) -> Result<bool>;
+
+}
+
+impl EntryCategory for Entry {
+
+ 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.to_string()))
+ .chain_err(|| CEK::HeaderWriteError)
+ .map(|_| ())
+ }
+
+ /// 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: &CategoryStore, s: &str) -> Result<()> {
+ trace!("Setting category '{}' checked", s);
+ let mut category = register
+ .get_category_by_name(s)?
+ .ok_or_else(|| CE::from_kind(CEK::CategoryDoesNotExist))?;
+
+ let _ = self.set_category(s)?;
+ let _ = self.add_internal_link(&mut category)?;
+
+ Ok(())
+ }
+
+ fn get_category(&self) -> Result<String> {
+ trace!("Getting category from '{}'", self.get_location());
+ self.get_header()
+ .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())
+ }
+
+}
diff --git a/lib/entry/libimagentrycategory/src/error.rs b/lib/entry/libimagentrycategory/src/error.rs
index 9cf446e..f9b90ff 100644
--- a/lib/entry/libimagentrycategory/src/error.rs
+++ b/lib/entry/libimagentrycategory/src/error.rs
@@ -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")
+ }
}
}
diff --git a/lib/entry/libimagentrycategory/src/iter.rs b/lib/entry/libimagentrycategory/src/iter.rs
new file mode 100644
index 0000000..a835b82
--- /dev/null
+++ b/lib/entry/libimagentrycategory/src/iter.rs
@@ -0,0 +1,116 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; version
+// 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+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
+///
+/// Iterates over Result<Category>
+///
+/// # Return values
+///
+/// In each iteration, a Option<Result<Category>> is returned. Error kinds are as follows:
+///
+/// * CategoryErrorKind::StoreReadError if a name could not be fetched from the store
+/// * CategoryErrorKind::HeaderReadError if the header of the fetched item couldn't be read
+/// * CategoryErrorKind::TypeError if the name could not be fetched because it is not a String
+///
+pub struct CategoryNameIter<'a>(&'a Store, StoreIdIterator);
+
+impl<'a> 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<String>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ // TODO: Optimize me with lazy_static
+ let query = CATEGORY_REGISTER_NAME_FIELD_PATH;
+
+ while let Some(sid) = self.1.next() {
+ if sid.is_in_collection(&["category"]) {
+ let func = |store: &Store| { // hack for returning Some(Result<_, _>)
+ store
+ .get(sid)?
+ .ok_or_else(|| CE::from_kind(CEK::StoreReadError))?
+ .get_header()
+ .read_string(query)
+ .chain_err(|| CEK::HeaderReadError)?
+ .ok_or_else(|| CE::from_kind(CEK::StoreReadError))
+ };
+
+ return Some(func(&self.0))
+ } // else continue
+ }
+
+ None
+ }
+}
+
+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
+ }
+}
diff --git a/lib/entry/libimagentrycategory/src/lib.rs b/lib/entry/libimagentrycategory/src/lib.rs
index 18eb690..0f0e118 100644
--- a/lib/entry/libimagentrycategory/src/lib.rs
+++ b/lib/entry/libimagentrycategory/src/lib.rs
@@ -38,18 +38,20 @@
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;
pub mod error;
-pub mod register;
+pub mod store;
+pub mod iter;
module_entry_path_mod!("category");
diff --git a/lib/entry/libimagentrycategory/src/register.rs b/lib/entry/libimagentrycategory/src/store.rs
index aa75ab4..e1fb929 100644
--- a/lib/entry/libimagentrycategory/src/register.rs
+++ b/lib/entry/libimagentrycategory/src/store.rs
@@ -26,24 +26,25 @@ 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";
/// Extension on the Store to make it a register for categories
///
/// The register writes files to the
-pub trait CategoryRegister {
+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<()>;
@@ -53,10 +54,11 @@ pub trait CategoryRegister {
}
-impl CategoryRegister for Store {
+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)
}
@@ -64,43 +66,31 @@ impl CategoryRegister for Store {
/// Create a category
///
/// Fails if the category already exists (returns false then)
- fn create_category(&self, name: &str) -> Result<bool> {
- use libimagstore::error::StoreErrorKind as SEK;
+ 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)?;
- let sid = mk_category_storeid(self.path().clone(), name)?;
+ entry.set_isflag::<IsCategory>()?;
+ let _ = entry
+ .get_header_mut()
+ .insert(CATEGORY_REGISTER_NAME_FIELD_PATH, Value::String(String::from(name)))?;
- match self.create(sid) {
- Ok(mut entry) => {
- let val = Value::String(String::from(name));
- entry.get_header_mut()
- .insert(CATEGORY_REGISTER_NAME_FIELD_PATH, val)
- .map(|opt| if opt.is_none() {
- debug!("Setting category header worked")
- } else {
- warn!("Setting category header replaced existing value: {:?}", opt);
- })
- .map(|_| true)
- .chain_err(|| CEK::HeaderWriteError)
- .chain_err(|| CEK::StoreWriteError)
- }
- Err(store_error) => if is_match!(store_error.kind(), &SEK::EntryAlreadyExists(_)) {
- Ok(false)
- } else {
- Err(store_error).chain_err(|| CEK::StoreWriteError)
- }
- }
+ 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).chain_err(|| CEK::StoreWriteError)
+ 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()))
}
@@ -109,6 +99,7 @@ impl CategoryRegister 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)
@@ -148,8 +139,6 @@ mod tests {
let res = store.create_category(category_name);
assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
- let res = res.unwrap();
- assert!(res);
}
#[test]
@@ -157,11 +146,10 @@ mod tests {
let category_name = "examplecategory";
let store = get_store();
- let res = store.create_category(category_name);
-
- assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
- let res = res.unwrap();
- assert!(res);
+ {
+ let res = store.create_category(category_name);
+ assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
+ }
let category = store.get(PathBuf::from(format!("category/{}", category_name)));
@@ -176,11 +164,11 @@ mod tests {
let _ = env_logger::try_init();
let category_name = "examplecategory";
let store = get_store();
- let res = store.create_category(category_name);
- assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
- let res = res.unwrap();
- assert!(res);
+ {
+ let res = store.create_category(category_name);
+ assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
+ }
let id = PathBuf::from(format!("category/{}", category_name));
println!("Trying: {:?}", id);
@@ -237,53 +225,3 @@ fn represents_category(store: &Store, sid: StoreId, name: &str) -> Result<bool>
})
}
-/// Iterator for Category names
-///
-/// Iterates over Result<Category>
-///
-/// # Return values
-///
-/// In each iteration, a Option<Result<Category>> is returned. Error kinds are as follows:
-///
-/// * CategoryErrorKind::StoreReadError if a name could not be fetched from the store
-/// * CategoryErrorKind::HeaderReadError if the header of the fetched item couldn't be read
-/// * CategoryErrorKind::TypeError if the name could not be fetched because it is not a String
-///
-pub struct CategoryNameIter<'a>(&'a Store, StoreIdIterator);
-
-impl<'a> CategoryNameIter<'a> {
-
- fn new(store: &'a Store, sidit: StoreIdIterator) -> CategoryNameIter<'a> {
- CategoryNameIter(store, sidit)
- }
-
-}
-
-impl<'a> Iterator for CategoryNameIter<'a> {
- type Item = Result<Category>;
-
- fn next(&mut self) -> Option<Self::Item> {
- // TODO: Optimize me with lazy_static
- let query = CATEGORY_REGISTER_NAME_FIELD_PATH;
-
- while let Some(sid) = self.1.next() {
- if sid.is_in_collection(&["category"]) {
- let func = |store: &Store| { // hack for returning Some(Result<_, _>)
- store
- .get(sid)?
- .ok_or_else(|| CE::from_kind(CEK::StoreReadError))?
- .get_header()
- .read_string(query)
- .chain_err(|| CEK::HeaderReadError)?
- .map(Category::from)
- .ok_or_else(|| CE::from_kind(CEK::StoreReadError))
- };
-
- return Some(func(&self.0))
- } // else continue
- }
-
- None
- }
-}
-