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 std::cmp::Ordering;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store; use libimagstore::store::Store;
use libimagstore::storeid::IntoStoreId; use libimagstore::storeid::IntoStoreId;
use libimagerror::trace::trace_error; use libimagerror::trace::trace_error;
@ -27,102 +28,136 @@ use chrono::offset::Local;
use chrono::Datelike; use chrono::Datelike;
use itertools::Itertools; use itertools::Itertools;
use chrono::naive::NaiveDateTime; use chrono::naive::NaiveDateTime;
use chrono::Timelike;
use entry::Entry; use entry::Entry;
use diaryid::DiaryId; use diaryid::DiaryId;
use error::DiaryError as DE;
use error::DiaryErrorKind as DEK; use error::DiaryErrorKind as DEK;
use error::MapErrInto;
use result::Result; use result::Result;
use iter::DiaryEntryIterator; use iter::DiaryEntryIterator;
use is_in_diary::IsInDiary; use iter::DiaryNameIterator;
#[derive(Debug)] trait Diary {
pub struct Diary<'a> {
store: &'a Store,
name: &'a str,
}
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> { /// Wrapper around Store::retrieve for DiaryId
Diary { fn retrieve(&self, id: DiaryId) -> Result<FileLockEntry>;
store: store,
name: name, /// Wrapper around Store::delete for DiaryId
} fn delete(&self, entry: Entry) -> Result<()>;
}
// create or get a new entry for today // create or get a new entry for today
pub fn new_entry_today(&self) -> Result<Entry> { fn new_entry_today(&self, diary_name: &str) -> Result<FileLockEntry>;
let dt = Local::now();
let ndt = dt.naive_local(); // create or get a new entry for now
let id = DiaryId::new(String::from(self.name), ndt.year(), ndt.month(), ndt.day(), 0, 0); fn new_entry_now(&self, diary_name: &str) -> Result<FileLockEntry>;
self.new_entry_by_id(id)
// 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> { /// Wrapper around Store::retrieve for DiaryId
self.retrieve(id.with_diary_name(String::from(self.name))) 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> { /// Wrapper around Store::delete for DiaryId
id.into_storeid() fn delete(&self, entry: Entry) -> Result<()> {
.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));
}
let id = entry.get_location().clone(); let id = entry.get_location().clone();
drop(entry); drop(entry);
self.store.delete(id) self.delete(id).map_err_into(DEK::StoreWriteError)
.map_err(|e| DE::new(DEK::StoreWriteError, Some(Box::new(e))))
} }
pub fn get_youngest_entry(&self) -> Option<Result<Entry>> { // create or get a new entry for today
match self.entries() { 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)), Err(e) => Some(Err(e)),
Ok(entries) => { Ok(entries) => {
entries.sorted_by(|a, b| { entries
match (a, b) { .map(|e| e.and_then(|e| e.diary_id()))
(&Ok(ref a), &Ok(ref b)) => { .sorted_by(|a, b| {
let a : NaiveDateTime = a.diary_id().into(); match (a, b) {
let b : NaiveDateTime = b.diary_id().into(); (&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)) => { (&Ok(_), &Err(ref e)) => {
trace_error(e); trace_error(e);
Ordering::Less Ordering::Less
}, },
(&Err(ref e), &Ok(_)) => { (&Err(ref e), &Ok(_)) => {
trace_error(e); trace_error(e);
Ordering::Greater Ordering::Greater
}, },
(&Err(ref e1), &Err(ref e2)) => { (&Err(ref e1), &Err(ref e2)) => {
trace_error(e1); trace_error(e1);
trace_error(e2); trace_error(e2);
Ordering::Equal Ordering::Equal
}, },
} }
}).into_iter().next() })
.into_iter()
//.map(|sidres| sidres.map(|sid| DiaryId::from_storeid(&sid)))
.next()
} }
} }
} }
pub fn name(&self) -> &'a str { /// Get all diary names
&self.name 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::DiaryId;
use diaryid::FromStoreId; use diaryid::FromStoreId;
use result::Result;
#[derive(Debug)] #[derive(Debug)]
pub struct Entry<'a>(FileLockEntry<'a>); pub struct Entry<'a>(FileLockEntry<'a>);
@ -57,8 +58,8 @@ impl<'a> Entry<'a> {
/// Get the diary id for this entry. /// Get the diary id for this entry.
/// ///
/// TODO: calls Option::unwrap() as it assumes that an existing Entry has an ID that is parsable /// TODO: calls Option::unwrap() as it assumes that an existing Entry has an ID that is parsable
pub fn diary_id(&self) -> DiaryId { pub fn diary_id(&self) -> Result<DiaryId> {
DiaryId::from_storeid(&self.0.get_location().clone()).unwrap() DiaryId::from_storeid(&self.0.get_location().clone())
} }
} }

View file

@ -28,7 +28,8 @@ generate_error_module!(
EntryNotInDiary => "Entry not in Diary", EntryNotInDiary => "Entry not in Diary",
IOError => "IO Error", IOError => "IO Error",
ViewError => "Error viewing diary entry", 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::store::Store;
use libimagstore::storeid::StoreIdIterator; use libimagstore::storeid::StoreIdIterator;
use libimagerror::trace::trace_error;
use libimagerror::into::IntoError;
use diaryid::DiaryId; use diaryid::DiaryId;
use diaryid::FromStoreId; use diaryid::FromStoreId;
@ -29,13 +31,14 @@ use is_in_diary::IsInDiary;
use entry::Entry as DiaryEntry; use entry::Entry as DiaryEntry;
use error::DiaryError as DE; use error::DiaryError as DE;
use error::DiaryErrorKind as DEK; use error::DiaryErrorKind as DEK;
use error::MapErrInto;
use result::Result; use result::Result;
use libimagerror::trace::trace_error; use is_in_diary::IsInDiary;
/// A iterator for iterating over diary entries /// A iterator for iterating over diary entries
pub struct DiaryEntryIterator<'a> { pub struct DiaryEntryIterator<'a> {
store: &'a Store, store: &'a Store,
name: &'a str, name: String,
iter: StoreIdIterator, iter: StoreIdIterator,
year: Option<i32>, year: Option<i32>,
@ -54,7 +57,7 @@ impl<'a> Debug for DiaryEntryIterator<'a> {
impl<'a> 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 { DiaryEntryIterator {
store: store, store: store,
name: diaryname, name: diaryname,
@ -97,7 +100,7 @@ impl<'a> Iterator for DiaryEntryIterator<'a> {
}; };
debug!("Next element: {:?}", next); 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); debug!("Seems to be in diary: {:?}", next);
let id = match DiaryId::from_storeid(&next) { let id = match DiaryId::from_storeid(&next) {
Ok(i) => i, 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::viewer::Viewer;
use libimagentryview::builtin::plain::PlainViewer; 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 /// 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. /// call some diary-type specific functions on the entries passed to this.
@ -48,8 +49,10 @@ impl DiaryViewer {
/// error. /// error.
pub fn view_entries<'a, I: Iterator<Item = Entry<'a>>>(&self, entries: I) -> Result<()> { pub fn view_entries<'a, I: Iterator<Item = Entry<'a>>>(&self, entries: I) -> Result<()> {
for entry in entries { for entry in entries {
let id = entry.diary_id(); match entry.diary_id() {
println!("{} :\n", id); Ok(id) => println!("{} :\n", id),
Err(e) => trace_error(&e),
}
let _ = try!(self.0 let _ = try!(self.0
.view_entry(&entry) .view_entry(&entry)
.map_err_into(DEK::ViewError) .map_err_into(DEK::ViewError)