use chrono::naive::NaiveDateTime;
use toml_query::delete::TomlValueDeleteExt;
use toml_query::insert::TomlValueInsertExt;
use toml_query::read::TomlValueReadTypeExt;
use toml::Value;
use libimagstore::store::Entry;
use libimagerror::errors::ErrorMsg as EM;
use failure::Error;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::err_msg;
use crate::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>>>;
}
const DATE_HEADER_LOCATION : &str = "datetime.value";
const DATE_RANGE_START_HEADER_LOCATION : &str = "datetime.range.start";
const DATE_RANGE_END_HEADER_LOCATION : &str = "datetime.range.end";
const DATE_FMT : &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(|_| ())
.context("Delete date error")
.map_err(Error::from)
}
fn read_date(&self) -> Result<NaiveDateTime> {
self.get_header()
.read_string(&DATE_HEADER_LOCATION)
.context("Error while reading date")?
.ok_or_else(|| err_msg("Error reading date"))?
.parse::<NaiveDateTime>()
.context("Datetime parse error")
.map_err(Error::from)
}
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))
.context(format_err!("Failed to insert header '{}' in '{}'",
DATE_HEADER_LOCATION,
self.get_location()))
.map_err(Error::from)
.map(|opt| opt.map(|stri| {
stri.as_str()
.ok_or_else(|| Error::from(EM::EntryHeaderTypeError))?
.parse::<NaiveDateTime>()
.context("Datetime parse error")
.map_err(Error::from)
}))
.context("Error setting date")
.map_err(Error::from)
}
fn delete_date_range(&mut self) -> Result<()> {
self
.get_header_mut()
.delete(&DATE_RANGE_START_HEADER_LOCATION)
.map(|_| ())
.context("Delete Datetime range error")?;
self.get_header_mut()
.delete(&DATE_RANGE_END_HEADER_LOCATION)
.map(|_| ())
.context("Delete Datetime range error")
.map_err(Error::from)
}
fn read_date_range(&self) -> Result<DateTimeRange> {
let start = self
.get_header()
.read_string(&DATE_RANGE_START_HEADER_LOCATION)
.context("Error while reading Datetime range")?
.ok_or_else(|| err_msg("Error reading date"))
.and_then(str_to_ndt)?;
let end = self
.get_header()
.read_string(&DATE_RANGE_START_HEADER_LOCATION)
.context("Error reading Datetime range")?
.ok_or_else(|| err_msg("Error reading date"))
.and_then(str_to_ndt)?;
DateTimeRange::new(start, end)
.context("Datetime Range error")
.map_err(Error::from)
}
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 = self
.get_header_mut()
.insert(&DATE_RANGE_START_HEADER_LOCATION, Value::String(start))
.map(|opt| opt.as_ref().map(val_to_ndt))
.context("Error setting Datetime range")?;
let opt_old_end = self
.get_header_mut()
.insert(&DATE_RANGE_END_HEADER_LOCATION, Value::String(end))
.map(|opt| opt.as_ref().map(val_to_ndt))
.context("Error setting Datetime range")?;
match (opt_old_start, opt_old_end) {
(Some(Ok(old_start)), Some(Ok(old_end))) => {
let dr = DateTimeRange::new(old_start, old_end)
.context("Error processing Datetime range")
.map_err(Error::from);
Ok(Some(dr))
},
(Some(Err(e)), _) => Err(e),
(_, Some(Err(e))) => Err(e),
_ => {
Ok(None)
},
}
}
}
#[inline]
fn str_to_ndt(v: String) -> Result<NaiveDateTime> {
v.parse::<NaiveDateTime>()
.context("Error parsing Datetime")
.map_err(Error::from)
}
#[inline]
fn val_to_ndt(v: &Value) -> Result<NaiveDateTime> {
v.as_str()
.ok_or_else(|| Error::from(EM::EntryHeaderTypeError))?
.parse::<NaiveDateTime>()
.context("Datetime parsing error")
.map_err(Error::from)
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
use libimagstore::store::Store;
use chrono::naive::NaiveDateTime;
use chrono::naive::NaiveDate;
use chrono::naive::NaiveTime;
use toml_query::read::TomlValueReadExt;
pub fn get_store() -> Store {
Store::new_inmemory(PathBuf::from("/"), &None).unwrap()
}
#[test]
fn test_set_date() {
let store = get_store();
#[allow(clippy::zero_prefixed_literal)]
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());
let hdr_field = entry.get_header().read(&DATE_HEADER_LOCATION);
assert!(hdr_field.is_ok());
let hdr_field = hdr_field.unwrap();
assert!(hdr_field.is_some());
let hdr_field = hdr_field.unwrap();
match *hdr_field {
Value::String(ref s) => assert_eq!("2000-01-02T03:04:05", s),
_ => panic!("Wrong header type"),
}
}
#[test]
#[allow(clippy::zero_prefixed_literal)]
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());
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();
#[allow(clippy::zero_prefixed_literal)]
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());
assert!(entry.delete_date().is_ok());
let hdr_field = entry.get_header().read(&DATE_HEADER_LOCATION);
assert!(hdr_field.is_ok());
let hdr_field = hdr_field.unwrap();
assert!(hdr_field.is_none());
}
}