Initial import

This commit is contained in:
Matthias Beyer 2017-09-01 20:41:09 +02:00
parent 167511afec
commit a71c9281ec
9 changed files with 511 additions and 0 deletions

View file

@ -24,6 +24,7 @@ members = [
"lib/domain/libimagbookmark", "lib/domain/libimagbookmark",
"lib/domain/libimagcontact", "lib/domain/libimagcontact",
"lib/domain/libimagdiary", "lib/domain/libimagdiary",
"lib/domain/libimaghabit",
"lib/domain/libimagmail", "lib/domain/libimagmail",
"lib/domain/libimagnotes", "lib/domain/libimagnotes",
"lib/domain/libimagtimetrack", "lib/domain/libimagtimetrack",

View file

@ -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" }

View file

@ -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)
}
}
}

View file

@ -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)
}

View file

@ -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>;
}

View file

@ -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)
}
}

View file

@ -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;

View file

@ -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>;

View file

@ -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)
}
}