summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-09-01 20:41:09 +0200
committerMatthias Beyer <mail@beyermatthias.de>2017-12-08 14:54:40 +0100
commita71c9281ec0d5b322594ebf8423ed9df55cfad1d (patch)
tree6dffef35fe04f3ca575fa63f622a351a5fc3c046
parent167511afecedd1edbbd49fa4e9e6b26c451bbe36 (diff)
Initial import
-rw-r--r--Cargo.toml1
-rw-r--r--lib/domain/libimaghabit/Cargo.toml26
-rw-r--r--lib/domain/libimaghabit/src/error.rs52
-rw-r--r--lib/domain/libimaghabit/src/habit.rs210
-rw-r--r--lib/domain/libimaghabit/src/instance.rs42
-rw-r--r--lib/domain/libimaghabit/src/iter.rs72
-rw-r--r--lib/domain/libimaghabit/src/lib.rs39
-rw-r--r--lib/domain/libimaghabit/src/result.rs25
-rw-r--r--lib/domain/libimaghabit/src/store.rs44
9 files changed, 511 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 7a0e8b3a..03400335 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,6 +24,7 @@ members = [
"lib/domain/libimagbookmark",
"lib/domain/libimagcontact",
"lib/domain/libimagdiary",
+ "lib/domain/libimaghabit",
"lib/domain/libimagmail",
"lib/domain/libimagnotes",
"lib/domain/libimagtimetrack",
diff --git a/lib/domain/libimaghabit/Cargo.toml b/lib/domain/libimaghabit/Cargo.toml
new file mode 100644
index 00000000..59d3949d
--- /dev/null
+++ b/lib/domain/libimaghabit/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "libimaghabit"
+version = "0.5.0"
+authors = ["Matthias Beyer <mail@beyermatthias.de>"]
+
+description = "Library for the imag core distribution"
+
+keywords = ["imag", "PIM", "personal", "information", "management"]
+readme = "../../../README.md"
+license = "LGPL-2.1"
+
+documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
+repository = "https://github.com/matthiasbeyer/imag"
+homepage = "http://imag-pim.org"
+
+[dependencies]
+chrono = "0.4"
+log = "0.3"
+toml = "0.4"
+toml-query = "0.4.0"
+error-chain = "0.11"
+is-match = "0.1"
+
+libimagstore = { version = "0.5.0", path = "../../../lib/core/libimagstore" }
+libimagerror = { version = "0.5.0", path = "../../../lib/core/libimagerror" }
+libimagentryedit = { version = "0.5.0", path = "../../../lib/entry/libimagentryedit" }
diff --git a/lib/domain/libimaghabit/src/error.rs b/lib/domain/libimaghabit/src/error.rs
new file mode 100644
index 00000000..0ec0ca3f
--- /dev/null
+++ b/lib/domain/libimaghabit/src/error.rs
@@ -0,0 +1,52 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 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
+//
+
+error_chain! {
+ types {
+ HabitError, HabitErrorKind, ResultExt, Result;
+ }
+
+ links {
+ StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind);
+ }
+
+ foreign_links {
+ TomlError(::toml_query::error::Error);
+ ChronoError(::chrono::format::ParseError);
+ }
+
+ errors {
+ HabitBuilderMissing(variable_name: &'static str) {
+ description("Habbit builder has not all required information")
+ display("Habit builder misses {}", variable_name)
+ }
+
+ HeaderFieldMissing(path: &'static str) {
+ description("Header field missing")
+ display("Header field missing: {}", path)
+ }
+
+ HeaderTypeError(path: &'static str, required_type: &'static str) {
+ description("Header type error")
+ display("Header type error: Expected {} at {}, found other type", required_type, path)
+ }
+
+ }
+}
+
diff --git a/lib/domain/libimaghabit/src/habit.rs b/lib/domain/libimaghabit/src/habit.rs
new file mode 100644
index 00000000..c69efcbc
--- /dev/null
+++ b/lib/domain/libimaghabit/src/habit.rs
@@ -0,0 +1,210 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 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::Value;
+use toml_query::read::TomlValueReadExt;
+use toml_query::insert::TomlValueInsertExt;
+use chrono::NaiveDateTime;
+use chrono::Local;
+use chrono::NaiveDate;
+
+use error::HabitError as HE;
+use error::HabitErrorKind as HEK;
+use error::*;
+use iter::HabitInstanceStoreIdIterator;
+
+use libimagstore::store::Store;
+use libimagstore::store::FileLockEntry;
+use libimagstore::store::Entry;
+use libimagstore::iter::get::StoreIdGetIteratorExtension;
+use libimagstore::storeid::IntoStoreId;
+
+pub const NAIVE_DATE_STRING_FORMAT : &'static str = "%Y-%m-%d";
+
+/// A HabitTemplate is a "template" of a habit. A user may define a habit "Eat vegetable".
+/// If the user ate a vegetable, she should create a HabitInstance from the Habit with the
+/// appropriate date (and optionally a comment) set.
+pub trait HabitTemplate : Sized {
+
+ /// Create an instance from this habit template
+ ///
+ /// By default creates an instance with the name of the template, the current time and the
+ /// current date and copies the comment from the template to the instance.
+ ///
+ /// It uses `Store::retrieve()` underneath
+ fn create_instance<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>>;
+
+ /// Check whether the instance is a habit by checking its headers for the habit data
+ fn is_habit_template(&self) -> Result<bool>;
+
+ fn habit_name(&self) -> Result<String>;
+ fn habit_date(&self) -> Result<String>;
+ fn habit_comment(&self) -> Result<String>;
+
+}
+
+impl HabitTemplate for Entry {
+
+ fn create_instance<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>> {
+ use module_path::ModuleEntryPath;
+ let date = date_to_string(&Local::today().naive_local());
+ let name = self.habit_name()?;
+ let comment = self.habit_comment()?;
+ let id = ModuleEntryPath::new(format!("instance/{}-{}", name, date))
+ .into_storeid()
+ .map_err(HE::from)?;
+
+ store.retrieve(id)
+ .map_err(From::from)
+ .and_then(|mut entry| {
+ {
+ let mut hdr = entry.get_header_mut();
+ try!(hdr.insert("habit.instance.name", Value::String(name)));
+ try!(hdr.insert("habit.instance.date", Value::String(date)));
+ try!(hdr.insert("habit.instance.comment", Value::String(comment)));
+ }
+ Ok(entry)
+ })
+ }
+
+ /// Check whether the instance is a habit by checking its headers for the habit data
+ fn is_habit_template(&self) -> Result<bool> {
+ [
+ "habit.template.name",
+ "habit.template.date",
+ "habit.template.comment",
+ ].iter().fold(Ok(true), |acc, path| acc.and_then(|b| {
+ self.get_header()
+ .read(path)
+ .map(|o| is_match!(o, Some(&Value::String(_))))
+ .map_err(From::from)
+ }))
+ }
+
+ fn habit_name(&self) -> Result<String> {
+ get_string_header_from_habit(self, "habit.template.name")
+ }
+
+ fn habit_date(&self) -> Result<String> {
+ get_string_header_from_habit(self, "habit.template.date")
+ }
+
+ fn habit_comment(&self) -> Result<String> {
+ get_string_header_from_habit(self, "habit.template.comment")
+ }
+
+}
+
+#[inline]
+fn get_string_header_from_habit(e: &Entry, path: &'static str) -> Result<String> {
+ match e.get_header().read(path)? {
+ Some(&Value::String(ref s)) => Ok(s.clone()),
+ Some(_) => Err(HEK::HeaderTypeError(path, "String").into()),
+ None => Err(HEK::HeaderFieldMissing(path).into()),
+ }
+}
+
+pub mod builder {
+ use toml::Value;
+ use toml_query::insert::TomlValueInsertExt;
+ use chrono::NaiveDate;
+
+ use libimagstore::store::Store;
+ use libimagstore::storeid::StoreId;
+ use libimagstore::storeid::IntoStoreId;
+ use libimagstore::store::FileLockEntry;
+
+ use error::HabitError as HE;
+ use error::HabitErrorKind as HEK;
+ use error::*;
+
+ use super::date_to_string;
+ use super::date_from_string;
+
+ pub struct HabitBuilder {
+ name: Option<String>,
+ comment: Option<String>,
+ date: Option<NaiveDate>,
+ }
+
+ impl HabitBuilder {
+
+ pub fn with_name(&mut self, name: String) -> &mut Self {
+ self.name = Some(name);
+ self
+ }
+
+ pub fn with_comment(&mut self, comment: String) -> &mut Self {
+ self.comment = Some(comment);
+ self
+ }
+
+ pub fn with_date(&mut self, date: NaiveDate) -> &mut Self {
+ self.date = Some(date);
+ self
+ }
+
+ pub fn build<'a>(self, store: &'a Store) -> Result<FileLockEntry<'a>> {
+ #[inline]
+ fn mkerr(s: &'static str) -> HE {
+ HE::from_kind(HEK::HabitBuilderMissing(s))
+ }
+
+ let name = try!(self.name.ok_or_else(|| mkerr("name")));
+ let dateobj = try!(self.date.ok_or_else(|| mkerr("date")));
+ let date = date_to_string(&dateobj);
+ let comment = self.comment.unwrap_or_else(|| String::new());
+ let sid = try!(build_habit_template_sid(&name));
+ let mut entry = try!(store.create(sid));
+
+ try!(entry.get_header_mut().insert("habit.template.name", Value::String(name)));
+ try!(entry.get_header_mut().insert("habit.template.date", Value::String(date)));
+ try!(entry.get_header_mut().insert("habit.template.comment", Value::String(comment)));
+
+ Ok(entry)
+ }
+
+ }
+
+ impl Default for HabitBuilder {
+ fn default() -> Self {
+ HabitBuilder {
+ name: None,
+ comment: None,
+ date: None,
+ }
+ }
+ }
+
+ /// Buld a StoreId for a Habit from a date object and a name of a habit
+ fn build_habit_template_sid(name: &String) -> Result<StoreId> {
+ use module_path::ModuleEntryPath;
+ ModuleEntryPath::new(format!("template/{}", name)).into_storeid().map_err(From::from)
+ }
+
+}
+
+fn date_to_string(ndt: &NaiveDate) -> String {
+ ndt.format(NAIVE_DATE_STRING_FORMAT).to_string()
+}
+
+fn date_from_string(s: &str) -> Result<NaiveDate> {
+ NaiveDate::parse_from_str(s, NAIVE_DATE_STRING_FORMAT).map_err(From::from)
+}
+
diff --git a/lib/domain/libimaghabit/src/instance.rs b/lib/domain/libimaghabit/src/instance.rs
new file mode 100644
index 00000000..917ae4f2
--- /dev/null
+++ b/lib/domain/libimaghabit/src/instance.rs
@@ -0,0 +1,42 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 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 chrono::NaiveDate;
+
+use error::Result;
+use habit::HabitTemplate;
+
+/// An instance of a habit is created for each time a habit is done.
+///
+/// # Note
+///
+/// A habit is a daily thing, so we only provide "date" as granularity for its time data.
+///
+pub trait HabitInstance {
+ /// Check whether the instance is a habit instance by checking its headers for the habit
+ /// data
+ fn is_habit_instance(&self) -> Result<bool>;
+
+ fn get_date(&self) -> Result<NaiveDate>;
+ fn set_date(&self, n: NaiveDate) -> Result<()>;
+ fn get_comment(&self) -> Result<String>;
+ fn set_comment(&self, c: String) -> Result<()>;
+ fn get_template_name(&self) -> Result<String>;
+}
+
diff --git a/lib/domain/libimaghabit/src/iter.rs b/lib/domain/libimaghabit/src/iter.rs
new file mode 100644
index 00000000..d3c96508
--- /dev/null
+++ b/lib/domain/libimaghabit/src/iter.rs
@@ -0,0 +1,72 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 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::Value;
+use toml_query::read::TomlValueReadExt;
+
+use libimagstore::store::FileLockEntry;
+use libimagstore::storeid::StoreIdIterator;
+use libimagstore::storeid::StoreId;
+
+use error::HabitError as HE;
+use error::HabitErrorKind as HEK;
+use error::*;
+
+pub struct HabitTemplateStoreIdIterator(StoreIdIterator);
+
+impl Iterator for HabitTemplateStoreIdIterator {
+ type Item = StoreId;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ while let Some(n) = self.0.next() {
+ if n.is_in_collection(&["habit", "template"]) {
+ return Some(n)
+ }
+ }
+ None
+ }
+}
+
+impl From<StoreIdIterator> for HabitTemplateStoreIdIterator {
+ fn from(sii: StoreIdIterator) -> Self {
+ HabitTemplateStoreIdIterator(sii)
+ }
+}
+
+pub struct HabitInstanceStoreIdIterator(StoreIdIterator);
+
+impl Iterator for HabitInstanceStoreIdIterator {
+ type Item = StoreId;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ while let Some(n) = self.0.next() {
+ if n.is_in_collection(&["habit", "instance"]) {
+ return Some(n)
+ }
+ }
+ None
+ }
+}
+
+impl From<StoreIdIterator> for HabitInstanceStoreIdIterator {
+ fn from(sii: StoreIdIterator) -> Self {
+ HabitInstanceStoreIdIterator(sii)
+ }
+}
+
diff --git a/lib/domain/libimaghabit/src/lib.rs b/lib/domain/libimaghabit/src/lib.rs
new file mode 100644
index 00000000..e1730678
--- /dev/null
+++ b/lib/domain/libimaghabit/src/lib.rs
@@ -0,0 +1,39 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 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
+//
+
+extern crate chrono;
+extern crate toml;
+extern crate toml_query;
+#[macro_use] extern crate log;
+#[macro_use] extern crate error_chain;
+#[macro_use] extern crate is_match;
+
+#[macro_use] extern crate libimagerror;
+#[macro_use] extern crate libimagstore;
+extern crate libimagentryedit;
+
+module_entry_path_mod!("habit");
+
+pub mod error;
+pub mod habit;
+pub mod instance;
+pub mod iter;
+pub mod result;
+pub mod store;
+
diff --git a/lib/domain/libimaghabit/src/result.rs b/lib/domain/libimaghabit/src/result.rs
new file mode 100644
index 00000000..83c503aa
--- /dev/null
+++ b/lib/domain/libimaghabit/src/result.rs
@@ -0,0 +1,25 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 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 std::result::Result as RResult;
+
+use error::HabitError;
+
+pub type Result<T> = RResult<T, HabitError>;
+
diff --git a/lib/domain/libimaghabit/src/store.rs b/lib/domain/libimaghabit/src/store.rs
new file mode 100644
index 00000000..6ac5e889
--- /dev/null
+++ b/lib/domain/libimaghabit/src/store.rs
@@ -0,0 +1,44 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 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 error::Result;
+use habit::builder::HabitBuilder;
+use iter::HabitTemplateStoreIdIterator;
+
+use libimagstore::store::Store;
+
+/// Extension trait for libimagstore::store::Store which is basically our Habit-Store
+pub trait HabitStore {
+
+ /// Create a new habit
+ fn create_habit(&self) -> HabitBuilder {
+ HabitBuilder::default()
+ }
+
+ /// Get an iterator over all habits
+ fn all_habit_templates(&self) -> Result<HabitTemplateStoreIdIterator>;
+
+}
+
+impl HabitStore for Store {
+ /// Get an iterator over all habits
+ fn all_habit_templates(&self) -> Result<HabitTemplateStoreIdIterator> {
+ self.entries().map(HabitTemplateStoreIdIterator::from).map_err(From::from)
+ }
+}