summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-06-07 17:00:52 +0200
committerGitHub <noreply@github.com>2017-06-07 17:00:52 +0200
commit16a12af873bdeae7eb2da94def40086fb239b1da (patch)
tree3d0ec80aaa0aa1076ba7b6902109e75a752e33b7
parent08f43c88111f90e5e2ac4980c572835d4a32fa8c (diff)
parent9982195714c4bea1b26c67d6e3f272d79362f084 (diff)
downloadimag-16a12af873bdeae7eb2da94def40086fb239b1da.zip
imag-16a12af873bdeae7eb2da94def40086fb239b1da.tar.gz
Merge pull request #941 from matthiasbeyer/libimagentrydate/init
libimagentrydate/init
-rw-r--r--Cargo.toml1
-rw-r--r--libimagentrydatetime/Cargo.toml33
-rw-r--r--libimagentrydatetime/src/datepath/accuracy.rs112
-rw-r--r--libimagentrydatetime/src/datepath/compiler.rs196
-rw-r--r--libimagentrydatetime/src/datepath/error.rs30
-rw-r--r--libimagentrydatetime/src/datepath/format.rs92
-rw-r--r--libimagentrydatetime/src/datepath/mod.rs26
-rw-r--r--libimagentrydatetime/src/datepath/result.rs25
-rw-r--r--libimagentrydatetime/src/datepath/to_store_id.rs39
-rw-r--r--libimagentrydatetime/src/datetime.rs321
-rw-r--r--libimagentrydatetime/src/error.rs39
-rw-r--r--libimagentrydatetime/src/lib.rs37
-rw-r--r--libimagentrydatetime/src/range.rs111
-rw-r--r--libimagentrydatetime/src/result.rs25
14 files changed, 1087 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 56298a5..df876c5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,7 @@ members = [
"libimagbookmark",
"libimagcounter",
"libimagdiary",
+ "libimagentrydatetime",
"libimagentryedit",
"libimagentryfilter",
"libimagentrylink",
diff --git a/libimagentrydatetime/Cargo.toml b/libimagentrydatetime/Cargo.toml
new file mode 100644
index 0000000..1c810e9
--- /dev/null
+++ b/libimagentrydatetime/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "libimagentrydatetime"
+version = "0.3.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.3"
+toml-query = "0.2"
+lazy_static = "0.2"
+toml = "0.4"
+
+[dev-dependencies]
+is-match = "0.1"
+
+[dependencies.libimagerror]
+path = "../libimagerror"
+
+[dependencies.libimagstore]
+path = "../libimagstore"
+
+[dependencies.libimagutil]
+path = "../libimagutil"
+
diff --git a/libimagentrydatetime/src/datepath/accuracy.rs b/libimagentrydatetime/src/datepath/accuracy.rs
new file mode 100644
index 0000000..47299c1
--- /dev/null
+++ b/libimagentrydatetime/src/datepath/accuracy.rs
@@ -0,0 +1,112 @@
+//
+// 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
+//
+
+/// The accuracy with which the compiler should compile the time specification
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub enum Accuracy {
+ Year,
+ Month,
+ Day,
+ Hour,
+ Minute,
+ Second
+}
+
+impl Accuracy {
+
+ /// Check whether the current setting includes a year.
+ pub fn has_year_accuracy(&self) -> bool {
+ match *self {
+ Accuracy::Year => true,
+ Accuracy::Month => true,
+ Accuracy::Day => true,
+ Accuracy::Hour => true,
+ Accuracy::Minute => true,
+ Accuracy::Second => true,
+ }
+ }
+
+ /// Check whether the current setting includes a month.
+ pub fn has_month_accuracy(&self) -> bool {
+ match *self {
+ Accuracy::Year => false,
+ Accuracy::Month => true,
+ Accuracy::Day => true,
+ Accuracy::Hour => true,
+ Accuracy::Minute => true,
+ Accuracy::Second => true,
+ }
+ }
+
+ /// Check whether the current setting includes a day.
+ pub fn has_day_accuracy(&self) -> bool {
+ match *self {
+ Accuracy::Year => false,
+ Accuracy::Month => false,
+ Accuracy::Day => true,
+ Accuracy::Hour => true,
+ Accuracy::Minute => true,
+ Accuracy::Second => true,
+ }
+ }
+
+ /// Check whether the current setting includes a hour.
+ pub fn has_hour_accuracy(&self) -> bool {
+ match *self {
+ Accuracy::Year => false,
+ Accuracy::Month => false,
+ Accuracy::Day => false,
+ Accuracy::Hour => true,
+ Accuracy::Minute => true,
+ Accuracy::Second => true,
+ }
+ }
+
+ /// Check whether the current setting includes a minute.
+ pub fn has_minute_accuracy(&self) -> bool {
+ match *self {
+ Accuracy::Year => false,
+ Accuracy::Month => false,
+ Accuracy::Day => false,
+ Accuracy::Hour => false,
+ Accuracy::Minute => true,
+ Accuracy::Second => true,
+ }
+ }
+
+ /// Check whether the current setting includes a second.
+ pub fn has_second_accuracy(&self) -> bool {
+ match *self {
+ Accuracy::Year => false,
+ Accuracy::Month => false,
+ Accuracy::Day => false,
+ Accuracy::Hour => false,
+ Accuracy::Minute => false,
+ Accuracy::Second => true,
+ }
+ }
+
+}
+
+impl Default for Accuracy {
+ fn default() -> Accuracy {
+ Accuracy::Second
+ }
+}
+
diff --git a/libimagentrydatetime/src/datepath/compiler.rs b/libimagentrydatetime/src/datepath/compiler.rs
new file mode 100644
index 0000000..f246eeb
--- /dev/null
+++ b/libimagentrydatetime/src/datepath/compiler.rs
@@ -0,0 +1,196 @@
+//
+// 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::path::PathBuf;
+
+use chrono::naive::datetime::NaiveDateTime;
+use chrono::Datelike;
+use chrono::Timelike;
+
+use libimagstore::storeid::StoreId;
+
+use datepath::accuracy::Accuracy;
+use datepath::format::Format;
+use datepath::result::Result;
+use datepath::error::DatePathCompilerErrorKind as DPCEK;
+use datepath::error::MapErrInto;
+
+pub struct DatePathCompiler {
+ accuracy : Accuracy,
+ format : Format,
+}
+
+impl DatePathCompiler {
+
+ pub fn new(accuracy: Accuracy, format: Format) -> DatePathCompiler {
+ DatePathCompiler {
+ accuracy : accuracy,
+ format : format,
+ }
+ }
+
+ /// Compile a NaiveDateTime object into a StoreId object.
+ ///
+ /// # More information
+ ///
+ /// See the documentations of the `Format` and the `Accuracy` types as well.
+ ///
+ /// # Warnings
+ ///
+ /// This does _not_ guarantee that the StoreId can be created, is not yet in the store or
+ /// anything else. Overall, this is just a `spec->path` compiler which is really stupid.
+ ///
+ /// # Return value
+ ///
+ /// The `StoreId` object on success.
+ ///
+ pub fn compile(&self, module_name: &str, datetime: &NaiveDateTime) -> Result<StoreId> {
+ let mut s = format!("{}/", module_name);
+
+ if self.accuracy.has_year_accuracy() /* always true */ {
+ s.push_str(&format!("{:04}", datetime.year()));
+ }
+
+ if self.accuracy.has_month_accuracy() {
+ match self.format {
+ Format::ElementIsFolder
+ | Format::DaysAreFolder
+ | Format::MonthIsFolder
+ | Format::YearIsFolder
+ => s.push_str(&format!("/{:02}", datetime.month())),
+ }
+ }
+
+ if self.accuracy.has_day_accuracy() {
+ match self.format {
+ Format::ElementIsFolder
+ | Format::DaysAreFolder
+ | Format::MonthIsFolder
+ => s.push_str(&format!("/{:02}", datetime.day())),
+ Format::YearIsFolder
+ => s.push_str(&format!("-{:02}", datetime.day())),
+ }
+ }
+
+ if self.accuracy.has_hour_accuracy() {
+ match self.format {
+ Format::ElementIsFolder
+ | Format::DaysAreFolder
+ => s.push_str(&format!("/{:02}", datetime.hour())),
+ Format::YearIsFolder
+ | Format::MonthIsFolder
+ => s.push_str(&format!("-{:02}", datetime.hour())),
+ }
+ }
+
+ if self.accuracy.has_minute_accuracy() {
+ match self.format {
+ Format::ElementIsFolder
+ => s.push_str(&format!("/{:02}", datetime.minute())),
+ Format::YearIsFolder
+ | Format::MonthIsFolder
+ | Format::DaysAreFolder
+ => s.push_str(&format!("-{:02}", datetime.minute())),
+ }
+ }
+
+ if self.accuracy.has_second_accuracy() {
+ match self.format {
+ Format::ElementIsFolder
+ => s.push_str(&format!("/{:02}", datetime.second())),
+ Format::YearIsFolder
+ | Format::MonthIsFolder
+ | Format::DaysAreFolder
+ => s.push_str(&format!("-{:02}", datetime.second())),
+ }
+ }
+
+ StoreId::new_baseless(PathBuf::from(s))
+ .map_err_into(DPCEK::StoreIdBuildFailed)
+ }
+
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use datepath::accuracy::Accuracy;
+ use datepath::format::Format;
+
+ use chrono::naive::date::NaiveDate;
+ use chrono::naive::datetime::NaiveDateTime;
+
+ #[test]
+ fn test_compiler_compile_simple() {
+ let dt = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0);
+ let compiler = DatePathCompiler::new(Accuracy::default(), Format::default());
+ let res = compiler.compile("testmodule", &dt);
+
+ assert!(res.is_ok());
+ let res = res.unwrap();
+
+ let s = res.to_str();
+
+ assert!(s.is_ok());
+ let s = s.unwrap();
+
+ assert_eq!("testmodule/2000/01/01/00/00/00", s);
+ }
+
+ fn test_accuracy(acc: Accuracy, dt: NaiveDateTime, modname: &str, matchstr: &str) {
+ let compiler = DatePathCompiler::new(acc, Format::default());
+ let res = compiler.compile(modname, &dt);
+
+ assert!(res.is_ok());
+ let res = res.unwrap();
+
+ let s = res.to_str();
+
+ assert!(s.is_ok());
+ let s = s.unwrap();
+
+ assert_eq!(matchstr, s);
+ }
+
+ #[test]
+ fn test_compiler_compile_year_accuracy() {
+ let dt = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0);
+ test_accuracy(Accuracy::Year, dt, "module", "module/2000");
+ }
+
+ #[test]
+ fn test_compiler_compile_month_accuracy() {
+ let dt = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0);
+ test_accuracy(Accuracy::Month, dt, "module", "module/2000/01");
+ }
+
+ #[test]
+ fn test_compiler_compile_day_accuracy() {
+ let dt = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0);
+ test_accuracy(Accuracy::Day, dt, "module", "module/2000/01/01");
+ }
+
+ #[test]
+ fn test_compiler_compile_year_paddning() {
+ let dt = NaiveDate::from_ymd(1, 1, 1).and_hms(0, 0, 0);
+ test_accuracy(Accuracy::Day, dt, "module", "module/0001/01/01");
+ }
+
+}
+
diff --git a/libimagentrydatetime/src/datepath/error.rs b/libimagentrydatetime/src/datepath/error.rs
new file mode 100644
index 0000000..1a16320
--- /dev/null
+++ b/libimagentrydatetime/src/datepath/error.rs
@@ -0,0 +1,30 @@
+//
+// 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 module for the DatePathCompiler type
+generate_error_module! {
+ generate_error_types!(DatePathCompilerError, DatePathCompilerErrorKind,
+ UnknownDatePathCompilerError => "Unknown DatePathCompiler error",
+ StoreIdBuildFailed => "Failed building StoreId object"
+ );
+}
+pub use self::error::DatePathCompilerError;
+pub use self::error::DatePathCompilerErrorKind;
+pub use self::error::MapErrInto;
+
diff --git a/libimagentrydatetime/src/datepath/format.rs b/libimagentrydatetime/src/datepath/format.rs
new file mode 100644
index 0000000..cc9e8c7
--- /dev/null
+++ b/libimagentrydatetime/src/datepath/format.rs
@@ -0,0 +1,92 @@
+//
+// 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
+//
+
+/// The format which should be used to compile the datetime spec into a StoreId object.
+///
+/// # Warning
+///
+/// These settings depend on the Accuracy settings of the compiler as well.
+///
+/// If the compiler settings only specify an accuracy of `Accuracy::Month`, a setting of
+/// `Format::ElementIsFolder` will result in the `month` beeing the file name.
+///
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub enum Format {
+ /// Each element of the Path is one folder level.
+ ///
+ /// This is the default.
+ ///
+ /// # Example
+ ///
+ /// The date "1st May of 2017, 14:15:16" will be compiled to `2017/05/01/14/15/16`.
+ ///
+ /// The second is the filename, then.
+ ///
+ /// # Usecase
+ ///
+ /// When expecting a lot of entries, this makes sure that the tree is fast-traversible and has
+ /// few files per folder (maximum 60).
+ ///
+ ElementIsFolder,
+
+ /// Each element from Year to Day is folder, below is filename.
+ ///
+ /// # Example
+ ///
+ /// The date "1st May of 2017, 14:15:16" will be compiled to `2017/05/01/14-15-16`.
+ ///
+ /// # Usecase
+ ///
+ /// When expecting few entries per day.
+ ///
+ DaysAreFolder,
+
+ /// Each element from Year to Month is folder, below is filename.
+ ///
+ /// # Example
+ ///
+ /// The date "1st May of 2017, 14:15:16" will be compiled to `2017/05/01T14-15-16`.
+ ///
+ /// # Usecase
+ ///
+ /// When expecting few entries per month.
+ ///
+ MonthIsFolder,
+
+ /// Each element from Year to Month is folder, below is filename.
+ ///
+ /// # Example
+ ///
+ /// The date "1st May of 2017, 14:15:16" will be compiled to `2017/05-01T14-15-16`.
+ ///
+ /// # Usecase
+ ///
+ /// When expecting few entries per year.
+ /// Might be never used.
+ ///
+ YearIsFolder,
+
+}
+
+impl Default for Format {
+ fn default() -> Format {
+ Format::ElementIsFolder
+ }
+}
+
diff --git a/libimagentrydatetime/src/datepath/mod.rs b/libimagentrydatetime/src/datepath/mod.rs
new file mode 100644
index 0000000..c964abf
--- /dev/null
+++ b/libimagentrydatetime/src/datepath/mod.rs
@@ -0,0 +1,26 @@
+//
+// 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
+//
+
+pub mod accuracy;
+pub mod compiler;
+pub mod error;
+pub mod format;
+pub mod result;
+pub mod to_store_id;
+
diff --git a/libimagentrydatetime/src/datepath/result.rs b/libimagentrydatetime/src/datepath/result.rs
new file mode 100644
index 0000000..2fc3350
--- /dev/null
+++ b/libimagentrydatetime/src/datepath/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
+//
+
+/// Result type for this module.
+use super::error::DatePathCompilerError as DPCE;
+use std::result::Result as RResult;
+
+pub type Result<T> = RResult<T, DPCE>;
+
diff --git a/libimagentrydatetime/src/datepath/to_store_id.rs b/libimagentrydatetime/src/datepath/to_store_id.rs
new file mode 100644
index 0000000..1d25921
--- /dev/null
+++ b/libimagentrydatetime/src/datepath/to_store_id.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
+//
+
+use chrono::naive::datetime::NaiveDateTime;
+
+use libimagstore::storeid::StoreId;
+use datepath::result::Result;
+use datepath::compiler::DatePathCompiler;
+
+//
+// Extension Trait for NaiveDateTime
+//
+
+pub trait ToStoreId {
+ fn to_store_id(&self, modname: &str, compiler: &DatePathCompiler) -> Result<StoreId>;
+}
+
+impl ToStoreId for NaiveDateTime {
+ fn to_store_id(&self, modname: &str, compiler: &DatePathCompiler) -> Result<StoreId> {
+ compiler.compile(modname, self)
+ }
+}
+
diff --git a/libimagentrydatetime/src/datetime.rs b/libimagentrydatetime/src/datetime.rs
new file mode 100644
index 0000000..ea1d175
--- /dev/null
+++ b/libimagentrydatetime/src/datetime.rs
@@ -0,0 +1,321 @@
+//
+// 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::naive::datetime::NaiveDateTime;
+use toml_query::delete::TomlValueDeleteExt;
+use toml_query::insert::TomlValueInsertExt;
+use toml_query::read::TomlValueReadExt;
+use toml::Value;
+
+use libimagstore::store::Entry;
+use libimagerror::into::IntoError;
+
+use error::DateErrorKind as DEK;
+use error::*;
+use result::Result;
+use range::DateTimeRange;
+
+pub trait EntryDate {
+
+ fn delete_date(&mut self) -> Result<()>;
+ fn read_date(&self) -> Result<NaiveDateTime>;
+ fn set_date(&mut self, d: NaiveDateTime) -> Result<Option<Result<NaiveDateTime>>>;
+
+ fn delete_date_range(&mut self) -> Result<()>;
+ fn read_date_range(&self) -> Result<DateTimeRange>;
+ fn set_date_range(&mut self, start: NaiveDateTime, end: NaiveDateTime) -> Result<Option<Result<DateTimeRange>>>;
+
+}
+
+lazy_static! {
+ static ref DATE_HEADER_LOCATION : &'static str = "date.value";
+ static ref DATE_RANGE_START_HEADER_LOCATION : &'static str = "date.range.start";
+ static ref DATE_RANGE_END_HEADER_LOCATION : &'static str = "date.range.end";
+ static ref DATE_FMT : &'static str = "%Y-%m-%dT%H:%M:%S";
+}
+
+impl EntryDate for Entry {
+
+ fn delete_date(&mut self) -> Result<()> {
+ self.get_header_mut()
+ .delete(&DATE_HEADER_LOCATION)
+ .map(|_| ())
+ .map_err_into(DEK::DeleteDateError)
+ }
+
+ fn read_date(&self) -> Result<NaiveDateTime> {
+ self.get_header()
+ .read(&DATE_HEADER_LOCATION)
+ .map_err_into(DEK::ReadDateError)
+ .and_then(|v| {
+ match v {
+ &Value::String(ref s) => s.parse::<NaiveDateTime>()
+ .map_err_into(DEK::DateTimeParsingError),
+ _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
+ }
+ })
+ }
+
+ /// Set a Date for this entry
+ ///
+ /// # Return value
+ ///
+ /// This function returns funny things, I know. But I find it more attractive to be explicit
+ /// what failed when here, instead of beeing nice to the user here.
+ ///
+ /// So here's a list how things are returned:
+ ///
+ /// - Err(_) if the inserting failed
+ /// - Ok(None) if the inserting succeeded and _did not replace an existing value_.
+ /// - Ok(Some(Ok(_))) if the inserting succeeded, but replaced an existing value which then got
+ /// parsed into a NaiveDateTime object
+ /// - Ok(Some(Err(_))) if the inserting succeeded, but replaced an existing value which then
+ /// got parsed into a NaiveDateTime object, where the parsing failed for some reason.
+ ///
+ fn set_date(&mut self, d: NaiveDateTime) -> Result<Option<Result<NaiveDateTime>>> {
+ let date = d.format(&DATE_FMT).to_string();
+
+ self.get_header_mut()
+ .insert(&DATE_HEADER_LOCATION, Value::String(date))
+ .map(|opt| opt.map(|stri| {
+ match stri {
+ Value::String(ref s) => s.parse::<NaiveDateTime>()
+ .map_err_into(DEK::DateTimeParsingError),
+ _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
+ }
+ }))
+ .map_err_into(DEK::SetDateError)
+ }
+
+
+ /// Deletes the date range
+ ///
+ /// # Warning
+ ///
+ /// First deletes the start, then the end. If the first operation fails, this might leave the
+ /// header in an inconsistent state.
+ ///
+ fn delete_date_range(&mut self) -> Result<()> {
+ let _ = try!(self
+ .get_header_mut()
+ .delete(&DATE_RANGE_START_HEADER_LOCATION)
+ .map(|_| ())
+ .map_err_into(DEK::DeleteDateTimeRangeError));
+
+ self.get_header_mut()
+ .delete(&DATE_RANGE_END_HEADER_LOCATION)
+ .map(|_| ())
+ .map_err_into(DEK::DeleteDateTimeRangeError)
+ }
+
+ fn read_date_range(&self) -> Result<DateTimeRange> {
+ let start = try!(self
+ .get_header()
+ .read(&DATE_RANGE_START_HEADER_LOCATION)
+ .map_err_into(DEK::ReadDateTimeRangeError)
+ .and_then(|v| {
+ match v {
+ &Value::String(ref s) => s.parse::<NaiveDateTime>()
+ .map_err_into(DEK::DateTimeParsingError),
+ _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
+ }
+ }));
+
+ let end = try!(self
+ .get_header()
+ .read(&DATE_RANGE_START_HEADER_LOCATION)
+ .map_err_into(DEK::ReadDateTimeRangeError)
+ .and_then(|v| {
+ match v {
+ &Value::String(ref s) => s.parse::<NaiveDateTime>()
+ .map_err_into(DEK::DateTimeParsingError),
+ _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
+ }
+ }));
+
+ DateTimeRange::new(start, end)
+ .map_err_into(DEK::DateTimeRangeError)
+ }
+
+ /// Set the date range
+ ///
+ /// # Warning
+ ///
+ /// This first sets the start, then the end. If the first operation fails, this might leave the
+ /// header in an inconsistent state.
+ ///
+ fn set_date_range(&mut self, start: NaiveDateTime, end: NaiveDateTime)
+ -> Result<Option<Result<DateTimeRange>>>
+ {
+ let start = start.format(&DATE_FMT).to_string();
+ let end = end.format(&DATE_FMT).to_string();
+
+ let opt_old_start = try!(self
+ .get_header_mut()
+ .insert(&DATE_RANGE_START_HEADER_LOCATION, Value::String(start))
+ .map(|opt| opt.map(|stri| {
+ match stri {
+ Value::String(ref s) => s.parse::<NaiveDateTime>()
+ .map_err_into(DEK::DateTimeParsingError),
+ _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
+ }
+ }))
+ .map_err_into(DEK::SetDateTimeRangeError));
+
+ let opt_old_end = try!(self
+ .get_header_mut()
+ .insert(&DATE_RANGE_END_HEADER_LOCATION, Value::String(end))
+ .map(|opt| opt.map(|stri| {
+ match stri {
+ Value::String(ref s) => s.parse::<NaiveDateTime>()
+ .map_err_into(DEK::DateTimeParsingError),
+ _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
+ }
+ }))
+ .map_err_into(DEK::SetDateTimeRangeError));
+
+ match (opt_old_start, opt_old_end) {
+ (Some(Ok(old_start)), Some(Ok(old_end))) => {
+ let dr = DateTimeRange::new(old_start, old_end)
+ .map_err_into(DEK::DateTimeRangeError);
+
+ Ok(Some(dr))
+ },
+
+ (Some(Err(e)), _) => Err(e),
+ (_, Some(Err(e))) => Err(e),
+ _ => {
+ Ok(None)
+ },
+ }
+ }
+
+}
+
+#[cfg(test)]
+mod tests {
+ use std::path::PathBuf;
+
+ use super::*;
+
+ use libimagstore::store::Store;
+
+ use chrono::naive::datetime::NaiveDateTime;
+ use chrono::naive::date::NaiveDate;
+ use chrono::naive::time::NaiveTime;
+
+ pub fn get_store() -> Store {
+ Store::new(PathBuf::from("/"), None).unwrap()
+ }
+
+ #[test]
+ fn test_set_date() {
+ let store = get_store();
+
+ let date = {
+ let date = NaiveDate::from_ymd(2000, 01, 02);
+ let time = NaiveTime::from_hms(03, 04, 05);
+
+ NaiveDateTime::new(date, time)
+ };
+
+ let mut entry = store.create(PathBuf::from("test")).unwrap();
+ let res = entry.set_date(date);
+
+ assert!(res.is_ok(), format!("Error: {:?}", res));
+ let res = res.unwrap();
+
+ assert!(res.is_none()); // There shouldn't be an existing value
+
+ // Check whether the header is set correctly
+
+ let hdr_field = entry.get_header().read(&DATE_HEADER_LOCATION);
+
+ assert!(hdr_field.is_ok());
+ let hdr_field = hdr_field.unwrap();
+
+ match *hdr_field {
+ Value::String(ref s) => assert_eq!("2000-01-02T03:04:05", s),
+ _ => assert!(false, "Wrong header type"),
+ }
+ }
+
+ #[test]
+ fn test_read_date() {
+ use chrono::Datelike;
+ use chrono::Timelike;
+
+ let store = get_store();
+
+ let date = {
+ let date = NaiveDate::from_ymd(2000, 01, 02);
+ let time = NaiveTime::from_hms(03, 04, 05);
+
+ NaiveDateTime::new(date, time)
+ };
+
+ let mut entry = store.create(PathBuf::from("test")).unwrap();
+ let res = entry.set_date(date);
+
+ assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
+ let res = res.unwrap();
+
+ assert!(res.is_none()); // There shouldn't be an existing value
+
+ // same as the test above ...
+
+ let d = entry.read_date();
+
+ assert!(d.is_ok(), format!("Expected Ok(_), got: {:?}", d));
+ let d = d.unwrap();
+
+ assert_eq!(d.date().year() , 2000);
+ assert_eq!(d.date().month() , 01);
+ assert_eq!(d.date().day() , 02);
+ assert_eq!(d.time().hour() , 03);
+ assert_eq!(d.time().minute() , 04);
+ assert_eq!(d.time().second() , 05);
+ }
+
+ #[test]
+ fn test_delete_date() {
+ let store = get_store();
+
+ let date = {
+ let date = NaiveDate::from_ymd(2000, 01, 02);
+ let time = NaiveTime::from_hms(03, 04, 05);
+
+ NaiveDateTime::new(date, time)
+ };
+
+ let mut entry = store.create(PathBuf::from("test")).unwrap();
+ let res = entry.set_date(date);
+
+ assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
+ let res = res.unwrap();
+ assert!(res.is_none()); // There shouldn't be an existing value
+
+ assert!(entry.delete_date().is_ok());
+
+ let hdr_field = entry.get_header().read(&DATE_HEADER_LOCATION);
+
+ assert!(hdr_field.is_err(), format!("Expected Err(_), got: {:?}", hdr_field));
+ }
+}
+
diff --git a/libimagentrydatetime/src/error.rs b/libimagentrydatetime/src/error.rs
new file mode 100644
index 0000000..0f0166a
--- /dev/null
+++ b/libimagentrydatetime/src/error.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
+//
+
+generate_error_module!(
+ generate_error_types!(DateError, DateErrorKind,
+ DeleteDateError => "Error deleting date",
+ ReadDateError => "Error reading date",
+ SetDateError => "Error setting date",
+ DeleteDateTimeRangeError => "Error deleting date-time range",
+ ReadDateTimeRangeError => "Error reading date-time range",
+ SetDateTimeRangeError => "Error setting date-time range",
+
+ DateTimeRangeError => "DateTime Range error",
+
+ DateHeaderFieldTypeError => "Expected the header field in the entry to have type 'String', but have other type",
+ DateTimeParsingError => "Error parsing DateTime"
+ );
+);
+
+pub use self::error::DateError;
+pub use self::error::DateErrorKind;
+pub use self::error::MapErrInto;
+
diff --git a/libimagentrydatetime/src/lib.rs b/libimagentrydatetime/src/lib.rs
new file mode 100644
index 0000000..fb0d73f
--- /dev/null
+++ b/libimagentrydatetime/src/lib.rs
@@ -0,0 +1,37 @@
+//
+// 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
+//
+
+#[macro_use] extern crate lazy_static;
+extern crate chrono;
+extern crate toml_query;
+extern crate toml;
+
+#[macro_use] extern crate libimagerror;
+extern crate libimagstore;
+extern crate libimagutil;
+
+#[cfg(test)]
+#[macro_use] extern crate is_match;
+
+pub mod datepath;
+pub mod datetime;
+pub mod error;
+pub mod range;
+pub mod result;
+
diff --git a/libimagentrydatetime/src/range.rs b/libimagentrydatetime/src/range.rs
new file mode 100644
index 0000000..3a94faf
--- /dev/null
+++ b/libimagentrydatetime/src/range.rs
@@ -0,0 +1,111 @@
+//
+// 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 types for range module
+pub mod error {
+ generate_error_module!(
+ generate_error_types!(DateTimeRangeError, DateTimeRangeErrorKind,
+ EndDateTimeBeforeStartDateTime => "End datetime is before start datetime"
+ );
+ );
+
+ pub use self::error::DateTimeRangeError;
+ pub use self::error::DateTimeRangeErrorKind;
+ pub use self::error::MapErrInto;
+}
+
+/// Result type for range module
+pub mod result {
+ use std::result::Result as RResult;
+ use super::error::DateTimeRangeError;
+
+ pub type Result<T> = RResult<T, DateTimeRangeError>;
+}
+
+use chrono::naive::datetime::NaiveDateTime;
+use libimagerror::into::IntoError;
+use self::result::Result;
+
+/// A Range between two dates
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct DateTimeRange(NaiveDateTime, NaiveDateTime);
+
+impl DateTimeRange {
+
+ /// Create a new DateTimeRange object
+ ///
+ /// # Return value
+ ///
+ /// Ok(DateTimeRange) if start is before end,
+ /// else Err(DateTimeRangeError)
+ ///
+ pub fn new(start: NaiveDateTime, end: NaiveDateTime) -> Result<DateTimeRange> {
+ use self::error::DateTimeRangeErrorKind as DTREK;
+ if start < end {
+ Ok(DateTimeRange(start, end))
+ } else {
+ Err(DTREK::EndDateTimeBeforeStartDateTime.into_error())
+ }
+ }
+
+}
+
+#[cfg(test)]
+mod tests {
+
+ use chrono::naive::datetime::NaiveDateTime;
+ use chrono::naive::date::NaiveDate;
+ use chrono::naive::time::NaiveTime;
+
+ use super::DateTimeRange;
+
+ #[test]
+ fn test_new_returns_error_if_start_after_end_date() {
+ let start = NaiveDateTime::new(
+ NaiveDate::from_ymd(2000, 02, 02),
+ NaiveTime::from_hms(12, 00, 02)
+ );
+
+ let end = NaiveDateTime::new(
+ NaiveDate::from_ymd(2000, 02, 02),
+ NaiveTime::from_hms(12, 00, 01)
+ );
+
+ let res = DateTimeRange::new(start, end);
+
+ assert!(res.is_err());
+ }
+
+ #[test]
+ fn test_new_returns_ok_if_start_is_before_end() {
+ let start = NaiveDateTime::new(
+ NaiveDate::from_ymd(2000, 02, 02),
+ NaiveTime::from_hms(12, 00, 01)
+ );
+
+ let end = NaiveDateTime::new(
+ NaiveDate::from_ymd(2000, 02, 02),
+ NaiveTime::from_hms(12, 00, 02)
+ );
+
+ let res = DateTimeRange::new(start, end);
+
+ assert!(res.is_ok());
+ }
+}
diff --git a/libimagentrydatetime/src/result.rs b/libimagentrydatetime/src/result.rs
new file mode 100644
index 0000000..d6d0a5a
--- /dev/null
+++ b/libimagentrydatetime/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::DateError;
+
+pub type Result<T> = RResult<T, DateError>;
+