diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2017-06-07 17:00:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-07 17:00:52 +0200 |
commit | 16a12af873bdeae7eb2da94def40086fb239b1da (patch) | |
tree | 3d0ec80aaa0aa1076ba7b6902109e75a752e33b7 | |
parent | 08f43c88111f90e5e2ac4980c572835d4a32fa8c (diff) | |
parent | 9982195714c4bea1b26c67d6e3f272d79362f084 (diff) | |
download | imag-16a12af873bdeae7eb2da94def40086fb239b1da.zip imag-16a12af873bdeae7eb2da94def40086fb239b1da.tar.gz |
Merge pull request #941 from matthiasbeyer/libimagentrydate/init
libimagentrydate/init
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | libimagentrydatetime/Cargo.toml | 33 | ||||
-rw-r--r-- | libimagentrydatetime/src/datepath/accuracy.rs | 112 | ||||
-rw-r--r-- | libimagentrydatetime/src/datepath/compiler.rs | 196 | ||||
-rw-r--r-- | libimagentrydatetime/src/datepath/error.rs | 30 | ||||
-rw-r--r-- | libimagentrydatetime/src/datepath/format.rs | 92 | ||||
-rw-r--r-- | libimagentrydatetime/src/datepath/mod.rs | 26 | ||||
-rw-r--r-- | libimagentrydatetime/src/datepath/result.rs | 25 | ||||
-rw-r--r-- | libimagentrydatetime/src/datepath/to_store_id.rs | 39 | ||||
-rw-r--r-- | libimagentrydatetime/src/datetime.rs | 321 | ||||
-rw-r--r-- | libimagentrydatetime/src/error.rs | 39 | ||||
-rw-r--r-- | libimagentrydatetime/src/lib.rs | 37 | ||||
-rw-r--r-- | libimagentrydatetime/src/range.rs | 111 | ||||
-rw-r--r-- | libimagentrydatetime/src/result.rs | 25 |
14 files changed, 1087 insertions, 0 deletions
@@ -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>; + |