Implement Diary as trait
This commit is contained in:
parent
22de22e6e5
commit
9c69645b69
5 changed files with 154 additions and 77 deletions
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
);
|
||||
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue