Merge pull request #941 from matthiasbeyer/libimagentrydate/init

libimagentrydate/init
This commit is contained in:
Matthias Beyer 2017-06-07 17:00:52 +02:00 committed by GitHub
commit 16a12af873
14 changed files with 1087 additions and 0 deletions

View File

@ -17,6 +17,7 @@ members = [
"libimagbookmark",
"libimagcounter",
"libimagdiary",
"libimagentrydatetime",
"libimagentryedit",
"libimagentryfilter",
"libimagentrylink",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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
//
/// Result type for this module.
use super::error::DatePathCompilerError as DPCE;
use std::result::Result as RResult;
pub type Result<T> = RResult<T, DPCE>;

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

View File

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

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

View File

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

View File

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

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::DateError;
pub type Result<T> = RResult<T, DateError>;