Implement Diary as trait

This commit is contained in:
Matthias Beyer 2017-08-28 21:58:43 +02:00
parent 22de22e6e5
commit 9c69645b69
5 changed files with 154 additions and 77 deletions

View file

@ -19,6 +19,7 @@
use std::cmp::Ordering;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagstore::storeid::IntoStoreId;
use libimagerror::trace::trace_error;
@ -27,102 +28,136 @@ use chrono::offset::Local;
use chrono::Datelike;
use itertools::Itertools;
use chrono::naive::NaiveDateTime;
use chrono::Timelike;
use entry::Entry;
use diaryid::DiaryId;
use error::DiaryError as DE;
use error::DiaryErrorKind as DEK;
use error::MapErrInto;
use result::Result;
use iter::DiaryEntryIterator;
use is_in_diary::IsInDiary;
use iter::DiaryNameIterator;
#[derive(Debug)]
pub struct Diary<'a> {
store: &'a Store,
name: &'a str,
}
trait Diary {
impl<'a> Diary<'a> {
/// Wrapper around Store::get for DiaryId
fn get(&self, id: DiaryId) -> Result<Option<FileLockEntry>>;
pub fn open(store: &'a Store, name: &'a str) -> Diary<'a> {
Diary {
store: store,
name: name,
}
}
/// Wrapper around Store::retrieve for DiaryId
fn retrieve(&self, id: DiaryId) -> Result<FileLockEntry>;
/// Wrapper around Store::delete for DiaryId
fn delete(&self, entry: Entry) -> Result<()>;
// create or get a new entry for today
pub fn new_entry_today(&self) -> Result<Entry> {
let dt = Local::now();
let ndt = dt.naive_local();
let id = DiaryId::new(String::from(self.name), ndt.year(), ndt.month(), ndt.day(), 0, 0);
self.new_entry_by_id(id)
fn new_entry_today(&self, diary_name: &str) -> Result<FileLockEntry>;
// create or get a new entry for now
fn new_entry_now(&self, diary_name: &str) -> Result<FileLockEntry>;
// Get an iterator for iterating over all entries of a Diary
fn entries(&self, diary_name: &str) -> Result<DiaryEntryIterator>;
fn get_youngest_entry_id(&self, diary_name: &str) -> Option<Result<DiaryId>>;
/// Get all diary names
fn diary_names(&self) -> Result<DiaryNameIterator>;
}
impl Diary for Store {
/// Wrapper around Store::get for DiaryId
fn get(&self, id: DiaryId) -> Result<Option<FileLockEntry>> {
id.into_storeid().and_then(|id| self.get(id)).map_err_into(DEK::StoreWriteError)
}
pub fn new_entry_by_id(&self, id: DiaryId) -> Result<Entry> {
self.retrieve(id.with_diary_name(String::from(self.name)))
/// Wrapper around Store::retrieve for DiaryId
fn retrieve(&self, id: DiaryId) -> Result<FileLockEntry> {
id.into_storeid().and_then(|id| self.retrieve(id)).map_err_into(DEK::StoreWriteError)
}
pub fn retrieve(&self, id: DiaryId) -> Result<Entry> {
id.into_storeid()
.and_then(|id| self.store.retrieve(id))
.map(|fle| Entry::new(fle))
.map_err(|e| DE::new(DEK::StoreWriteError, Some(Box::new(e))))
}
// Get an iterator for iterating over all entries
pub fn entries(&self) -> Result<DiaryEntryIterator<'a>> {
self.store
.retrieve_for_module("diary")
.map(|iter| DiaryEntryIterator::new(self.name, self.store, iter))
.map_err(|e| DE::new(DEK::StoreReadError, Some(Box::new(e))))
}
pub fn delete_entry(&self, entry: Entry) -> Result<()> {
if !entry.is_in_diary(self.name) {
return Err(DE::new(DEK::EntryNotInDiary, None));
}
/// Wrapper around Store::delete for DiaryId
fn delete(&self, entry: Entry) -> Result<()> {
let id = entry.get_location().clone();
drop(entry);
self.store.delete(id)
.map_err(|e| DE::new(DEK::StoreWriteError, Some(Box::new(e))))
self.delete(id).map_err_into(DEK::StoreWriteError)
}
pub fn get_youngest_entry(&self) -> Option<Result<Entry>> {
match self.entries() {
// create or get a new entry for today
fn new_entry_today(&self, diary_name: &str) -> Result<FileLockEntry> {
let dt = Local::now();
let ndt = dt.naive_local();
let id = DiaryId::new(String::from(diary_name), ndt.year(), ndt.month(), ndt.day(), 0, 0);
Diary::retrieve(self, id)
}
// create or get a new entry for today
fn new_entry_now(&self, diary_name: &str) -> Result<FileLockEntry> {
let dt = Local::now();
let ndt = dt.naive_local();
let id = DiaryId::new(String::from(diary_name),
ndt.year(),
ndt.month(),
ndt.day(),
ndt.minute(),
ndt.second());
Diary::retrieve(self, id)
}
// Get an iterator for iterating over all entries
fn entries(&self, diary_name: &str) -> Result<DiaryEntryIterator> {
self.retrieve_for_module("diary")
.map(|iter| DiaryEntryIterator::new(self, String::from(diary_name), iter))
.map_err_into(DEK::StoreReadError)
}
fn get_youngest_entry_id(&self, diary_name: &str) -> Option<Result<DiaryId>> {
match Diary::entries(self, diary_name) {
Err(e) => Some(Err(e)),
Ok(entries) => {
entries.sorted_by(|a, b| {
match (a, b) {
(&Ok(ref a), &Ok(ref b)) => {
let a : NaiveDateTime = a.diary_id().into();
let b : NaiveDateTime = b.diary_id().into();
entries
.map(|e| e.and_then(|e| e.diary_id()))
.sorted_by(|a, b| {
match (a, b) {
(&Ok(ref a), &Ok(ref b)) => {
let a : NaiveDateTime = a.clone().into();
let b : NaiveDateTime = b.clone().into();
a.cmp(&b)
},
a.cmp(&b)
},
(&Ok(_), &Err(ref e)) => {
trace_error(e);
Ordering::Less
},
(&Err(ref e), &Ok(_)) => {
trace_error(e);
Ordering::Greater
},
(&Err(ref e1), &Err(ref e2)) => {
trace_error(e1);
trace_error(e2);
Ordering::Equal
},
}
}).into_iter().next()
(&Ok(_), &Err(ref e)) => {
trace_error(e);
Ordering::Less
},
(&Err(ref e), &Ok(_)) => {
trace_error(e);
Ordering::Greater
},
(&Err(ref e1), &Err(ref e2)) => {
trace_error(e1);
trace_error(e2);
Ordering::Equal
},
}
})
.into_iter()
//.map(|sidres| sidres.map(|sid| DiaryId::from_storeid(&sid)))
.next()
}
}
}
pub fn name(&self) -> &'a str {
&self.name
/// Get all diary names
fn diary_names(&self) -> Result<DiaryNameIterator> {
self.retrieve_for_module("diary")
.map_err_into(DEK::StoreReadError)
.map(DiaryNameIterator::new)
}
}

View file

@ -27,6 +27,7 @@ use libimagrt::runtime::Runtime;
use diaryid::DiaryId;
use diaryid::FromStoreId;
use result::Result;
#[derive(Debug)]
pub struct Entry<'a>(FileLockEntry<'a>);
@ -57,8 +58,8 @@ impl<'a> Entry<'a> {
/// Get the diary id for this entry.
///
/// TODO: calls Option::unwrap() as it assumes that an existing Entry has an ID that is parsable
pub fn diary_id(&self) -> DiaryId {
DiaryId::from_storeid(&self.0.get_location().clone()).unwrap()
pub fn diary_id(&self) -> Result<DiaryId> {
DiaryId::from_storeid(&self.0.get_location().clone())
}
}

View file

@ -28,7 +28,8 @@ generate_error_module!(
EntryNotInDiary => "Entry not in Diary",
IOError => "IO Error",
ViewError => "Error viewing diary entry",
IdParseError => "Error while parsing ID"
IdParseError => "Error while parsing ID",
DiaryNameFindingError => "Error while finding a diary name"
);
);

View file

@ -22,6 +22,8 @@ use std::result::Result as RResult;
use libimagstore::store::Store;
use libimagstore::storeid::StoreIdIterator;
use libimagerror::trace::trace_error;
use libimagerror::into::IntoError;
use diaryid::DiaryId;
use diaryid::FromStoreId;
@ -29,13 +31,14 @@ use is_in_diary::IsInDiary;
use entry::Entry as DiaryEntry;
use error::DiaryError as DE;
use error::DiaryErrorKind as DEK;
use error::MapErrInto;
use result::Result;
use libimagerror::trace::trace_error;
use is_in_diary::IsInDiary;
/// A iterator for iterating over diary entries
pub struct DiaryEntryIterator<'a> {
store: &'a Store,
name: &'a str,
name: String,
iter: StoreIdIterator,
year: Option<i32>,
@ -54,7 +57,7 @@ impl<'a> Debug for DiaryEntryIterator<'a> {
impl<'a> DiaryEntryIterator<'a> {
pub fn new(diaryname: &'a str, store: &'a Store, iter: StoreIdIterator) -> DiaryEntryIterator<'a> {
pub fn new(store: &'a Store, diaryname: String, iter: StoreIdIterator) -> DiaryEntryIterator<'a> {
DiaryEntryIterator {
store: store,
name: diaryname,
@ -97,7 +100,7 @@ impl<'a> Iterator for DiaryEntryIterator<'a> {
};
debug!("Next element: {:?}", next);
if next.is_in_diary(self.name) {
if next.is_in_diary(&self.name) {
debug!("Seems to be in diary: {:?}", next);
let id = match DiaryId::from_storeid(&next) {
Ok(i) => i,
@ -130,3 +133,37 @@ impl<'a> Iterator for DiaryEntryIterator<'a> {
}
/// Get diary names.
///
/// # Warning
///
/// Does _not_ run a `unique` on the iterator!
pub struct DiaryNameIterator(StoreIdIterator);
impl DiaryNameIterator {
pub fn new(s: StoreIdIterator) -> DiaryNameIterator {
DiaryNameIterator(s)
}
}
impl Iterator for DiaryNameIterator {
type Item = Result<String>;
fn next(&mut self) -> Option<Self::Item> {
self.0
.next()
.map(|s| {
s.to_str()
.map_err_into(DEK::DiaryNameFindingError)
.and_then(|s| {
s.split("diary/")
.nth(1)
.and_then(|n| n.split("/").nth(0).map(String::from))
.ok_or(DEK::DiaryNameFindingError.into_error())
})
})
}
}

View file

@ -26,6 +26,7 @@ use result::Result;
use libimagentryview::viewer::Viewer;
use libimagentryview::builtin::plain::PlainViewer;
use libimagerror::trace::trace_error;
/// This viewer does _not_ implement libimagentryview::viewer::Viewer because we need to be able to
/// call some diary-type specific functions on the entries passed to this.
@ -48,8 +49,10 @@ impl DiaryViewer {
/// error.
pub fn view_entries<'a, I: Iterator<Item = Entry<'a>>>(&self, entries: I) -> Result<()> {
for entry in entries {
let id = entry.diary_id();
println!("{} :\n", id);
match entry.diary_id() {
Ok(id) => println!("{} :\n", id),
Err(e) => trace_error(&e),
}
let _ = try!(self.0
.view_entry(&entry)
.map_err_into(DEK::ViewError)