Merge pull request #1065 from matthiasbeyer/libimagnotes/do-not-wrap

libimagnotes: Do not wrap store types.
This commit is contained in:
Matthias Beyer 2017-09-15 21:00:24 +02:00 committed by GitHub
commit be8a3d1242
8 changed files with 264 additions and 196 deletions

View File

@ -28,18 +28,18 @@ extern crate libimagentryedit;
extern crate libimagerror;
extern crate libimagutil;
use std::process::exit;
use itertools::Itertools;
use libimagentryedit::edit::Edit;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagnotes::note::Note;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagnotes::notestore::*;
use libimagerror::trace::MapErrTrace;
use libimagutil::info_result::*;
use libimagutil::warn_result::WarnResult;
mod ui;
use ui::build_ui;
@ -71,49 +71,54 @@ fn name_from_cli(rt: &Runtime, subcmd: &str) -> String {
fn create(rt: &Runtime) {
let name = name_from_cli(rt, "create");
Note::new(rt.store(), name.clone(), String::new()).map_err_trace().ok();
let mut note = rt
.store()
.new_note(name.clone(), String::new())
.map_err_trace_exit(1)
.unwrap();
if rt.cli().subcommand_matches("create").unwrap().is_present("edit") &&
!edit_entry(rt, name) {
exit(1);
if rt.cli().subcommand_matches("create").unwrap().is_present("edit") {
let _ = note
.edit_content(rt)
.map_warn_err_str("Editing failed")
.map_err_trace_exit(1);
}
}
fn delete(rt: &Runtime) {
Note::delete(rt.store(), String::from(name_from_cli(rt, "delete")))
.map_err_trace()
let _ = rt.store()
.delete_note(name_from_cli(rt, "delete"))
.map_info_str("Ok")
.ok();
.map_err_trace_exit(1);
}
fn edit(rt: &Runtime) {
edit_entry(rt, name_from_cli(rt, "edit"));
}
fn edit_entry(rt: &Runtime, name: String) -> bool {
let mut note = match Note::get(rt.store(), name) {
Ok(Some(note)) => note,
Ok(None) => {
warn!("Cannot edit nonexistent Note");
return false
},
Err(e) => {
trace_error(&e);
warn!("Cannot edit nonexistent Note");
return false
},
};
note.edit_content(rt).map_err_trace().map_warn_err_str("Editing failed").is_ok()
let name = name_from_cli(rt, "edit");
let _ = rt
.store()
.get_note(name.clone())
.map_err_trace_exit(1)
.unwrap()
.map(|mut note| {
let _ = note
.edit_content(rt)
.map_warn_err_str("Editing failed")
.map_err_trace_exit(1);
})
.unwrap_or_else(|| {
error!("Cannot find note with name '{}'", name);
});
}
fn list(rt: &Runtime) {
use std::cmp::Ordering;
Note::all_notes(rt.store())
rt.store()
.all_notes()
.map_err_trace_exit(1)
.map(|iter| {
let notes = iter.filter_map(|note| note.map_err_trace().ok())
let notes = iter
.filter_map(|noteid| rt.store().get(noteid).map_err_trace_exit(1).unwrap())
.sorted_by(|note_a, note_b| {
if let (Ok(a), Ok(b)) = (note_a.get_name(), note_b.get_name()) {
return a.cmp(&b)

View File

@ -58,6 +58,8 @@ This section contains the changelog from the last release to the next release.
* The "toml-query" dependency was updated to 0.3.1
* `imag-timetrack track` is now able to parse "now", date-only start/stop
dates and date-time start/stop times.
* `libimagnotes` does not longer wrap store types but extend them.
* Stats
## 0.3.0

View File

@ -0,0 +1,50 @@
//
// 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 libimagstore::storeid::StoreId;
use libimagstore::storeid::StoreIdIterator;
use notestoreid::*;
#[derive(Debug)]
pub struct NoteIterator(StoreIdIterator);
impl NoteIterator {
pub fn new(iditer: StoreIdIterator) -> NoteIterator {
NoteIterator(iditer)
}
}
impl Iterator for NoteIterator {
type Item = StoreId;
fn next(&mut self) -> Option<Self::Item> {
while let Some(n) = self.0.next() {
if n.is_note_id() {
return Some(n);
}
}
None
}
}

View File

@ -49,4 +49,7 @@ module_entry_path_mod!("notes");
pub mod error;
pub mod note;
pub mod notestore;
pub mod notestoreid;
pub mod iter;

View File

@ -17,80 +17,36 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use std::collections::BTreeMap;
use std::ops::Deref;
use toml::Value;
use libimagrt::runtime::Runtime;
use libimagentryedit::edit::Edit;
use libimagentryedit::error::Result as EditResult;
use libimagstore::storeid::IntoStoreId;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::StoreIdIterator;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagstore::store::Entry;
use toml_query::read::TomlValueReadExt;
use toml_query::set::TomlValueSetExt;
use module_path::ModuleEntryPath;
use error::Result;
use error::NoteErrorKind as NEK;
use error::NoteError as NE;
use error::ResultExt;
#[derive(Debug)]
pub struct Note<'a> {
entry: FileLockEntry<'a>,
pub trait Note {
fn set_name(&mut self, n: String) -> Result<()>;
fn get_name(&self) -> Result<String>;
fn set_text(&mut self, n: String);
fn get_text(&self) -> &String;
}
impl<'a> Note<'a> {
impl Note for Entry {
pub fn new(store: &Store, name: String, text: String) -> Result<Note> {
use std::ops::DerefMut;
debug!("Creating new Note: '{}'", name);
let fle = {
let mut lockentry = try!(ModuleEntryPath::new(name.clone())
.into_storeid()
.and_then(|id| store.create(id))
.chain_err(|| NEK::StoreWriteError));
{
let entry = lockentry.deref_mut();
{
let header = entry.get_header_mut();
let _ = header
.set("note", Value::Table(BTreeMap::new()))
.chain_err(|| NEK::StoreWriteError);
let _ = header
.set("note.name", Value::String(name))
.chain_err(|| NEK::StoreWriteError);
}
*entry.get_content_mut() = text;
}
lockentry
};
Ok(Note { entry: fle })
}
pub fn set_name(&mut self, n: String) -> Result<()> {
self.entry
.get_header_mut()
fn set_name(&mut self, n: String) -> Result<()> {
self.get_header_mut()
.set("note.name", Value::String(n))
.chain_err(|| NEK::StoreWriteError)
.map(|_| ())
}
pub fn get_name(&self) -> Result<String> {
let header = self.entry.get_header();
match header.read("note.name") {
fn get_name(&self) -> Result<String> {
match self.get_header().read("note.name") {
Ok(Some(&Value::String(ref s))) => Ok(s.clone()),
Ok(_) => {
Err(NE::from_kind(NEK::HeaderTypeError)).chain_err(|| NEK::StoreReadError)
@ -99,104 +55,14 @@ impl<'a> Note<'a> {
}
}
pub fn set_text(&mut self, n: String) {
*self.entry.get_content_mut() = n
fn set_text(&mut self, n: String) {
*self.get_content_mut() = n
}
pub fn get_text(&self) -> &String {
self.entry.get_content()
}
pub fn delete(store: &Store, name: String) -> Result<()> {
ModuleEntryPath::new(name)
.into_storeid()
.and_then(|id| store.delete(id))
.chain_err(|| NEK::StoreWriteError)
}
pub fn retrieve(store: &Store, name: String) -> Result<Note> {
ModuleEntryPath::new(name)
.into_storeid()
.and_then(|id| store.retrieve(id))
.chain_err(|| NEK::StoreWriteError)
.map(|entry| Note { entry: entry })
}
pub fn get(store: &Store, name: String) -> Result<Option<Note>> {
ModuleEntryPath::new(name)
.into_storeid()
.and_then(|id| store.get(id))
.chain_err(|| NEK::StoreWriteError)
.map(|o| o.map(|entry| Note { entry: entry }))
}
pub fn all_notes(store: &Store) -> Result<NoteIterator> {
store.retrieve_for_module("notes")
.map(|iter| NoteIterator::new(store, iter))
.chain_err(|| NEK::StoreReadError)
fn get_text(&self) -> &String {
self.get_content()
}
}
impl<'a> Edit for Note<'a> {
fn edit_content(&mut self, rt: &Runtime) -> EditResult<()> {
self.entry.edit_content(rt)
}
}
trait FromStoreId {
fn from_storeid(&Store, StoreId) -> Result<Note>;
}
impl<'a> FromStoreId for Note<'a> {
fn from_storeid(store: &Store, id: StoreId) -> Result<Note> {
debug!("Loading note from storeid: '{:?}'", id);
match store.retrieve(id) {
Err(e) => Err(e).chain_err(|| NEK::StoreReadError),
Ok(entry) => Ok(Note { entry: entry }),
}
}
}
impl<'a> Deref for Note<'a> {
type Target = FileLockEntry<'a>;
fn deref(&self) -> &FileLockEntry<'a> {
&self.entry
}
}
#[derive(Debug)]
pub struct NoteIterator<'a> {
store: &'a Store,
iditer: StoreIdIterator,
}
impl<'a> NoteIterator<'a> {
pub fn new(store: &'a Store, iditer: StoreIdIterator) -> NoteIterator<'a> {
NoteIterator {
store: store,
iditer: iditer,
}
}
}
impl<'a> Iterator for NoteIterator<'a> {
type Item = Result<Note<'a>>;
fn next(&mut self) -> Option<Result<Note<'a>>> {
self.iditer
.next()
.map(|id| Note::from_storeid(self.store, id))
}
}

View File

@ -0,0 +1,108 @@
//
// 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::collections::BTreeMap;
use toml::Value;
use libimagstore::storeid::IntoStoreId;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use toml_query::set::TomlValueSetExt;
use module_path::ModuleEntryPath;
use error::Result;
use error::NoteErrorKind as NEK;
use error::ResultExt;
use iter::*;
pub trait NoteStore<'a> {
fn new_note(&'a self, name: String, text: String) -> Result<FileLockEntry<'a>>;
fn delete_note(&'a self, name: String) -> Result<()>;
fn retrieve_note(&'a self, name: String) -> Result<FileLockEntry<'a>>;
fn get_note(&'a self, name: String) -> Result<Option<FileLockEntry<'a>>>;
fn all_notes(&'a self) -> Result<NoteIterator>;
}
impl<'a> NoteStore<'a> for Store {
fn new_note(&'a self, name: String, text: String) -> Result<FileLockEntry<'a>> {
use std::ops::DerefMut;
debug!("Creating new Note: '{}'", name);
let fle = {
let mut lockentry = try!(ModuleEntryPath::new(name.clone())
.into_storeid()
.and_then(|id| self.create(id))
.chain_err(|| NEK::StoreWriteError));
{
let entry = lockentry.deref_mut();
{
let header = entry.get_header_mut();
let _ = header
.set("note", Value::Table(BTreeMap::new()))
.chain_err(|| NEK::StoreWriteError);
let _ = header
.set("note.name", Value::String(name))
.chain_err(|| NEK::StoreWriteError);
}
*entry.get_content_mut() = text;
}
lockentry
};
Ok(fle)
}
fn delete_note(&'a self, name: String) -> Result<()> {
ModuleEntryPath::new(name)
.into_storeid()
.and_then(|id| self.delete(id))
.chain_err(|| NEK::StoreWriteError)
}
fn retrieve_note(&'a self, name: String) -> Result<FileLockEntry<'a>> {
ModuleEntryPath::new(name)
.into_storeid()
.and_then(|id| self.retrieve(id))
.chain_err(|| NEK::StoreWriteError)
}
fn get_note(&'a self, name: String) -> Result<Option<FileLockEntry<'a>>> {
ModuleEntryPath::new(name)
.into_storeid()
.and_then(|id| self.get(id))
.chain_err(|| NEK::StoreWriteError)
}
fn all_notes(&'a self) -> Result<NoteIterator> {
self.retrieve_for_module("notes")
.map(NoteIterator::new)
.chain_err(|| NEK::StoreReadError)
}
}

View File

@ -0,0 +1,32 @@
//
// 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 libimagstore::storeid::StoreId;
pub trait NoteStoreId {
fn is_note_id(&self) -> bool;
}
impl NoteStoreId for StoreId {
fn is_note_id(&self) -> bool {
self.is_in_collection(&["notes"])
}
}

View File

@ -20,8 +20,8 @@
use libimagstore::store::Entry;
use libimagstore::store::Store;
use libimagentrylink::internal::InternalLinker;
use libimagnotes::note::Note;
use libimagnotes::note::NoteIterator;
use libimagnotes::notestore::NoteStore;
use libimagnotes::iter::NoteIterator;
use libimagstore::storeid::StoreIdIterator;
use error::Result;
@ -43,8 +43,8 @@ impl<'a> AnnotationFetcher<'a> for Store {
/// Wrapper around `Note::all_notes()` of `libimagnotes` which filters out normal notes and
/// leaves only annotations in the iterator.
fn all_annotations(&'a self) -> Result<AnnotationIter<'a>> {
Note::all_notes(self)
.map(|iter| AnnotationIter::new(iter))
NoteStore::all_notes(self)
.map(|iter| AnnotationIter::new(iter, self))
.chain_err(|| AEK::StoreReadError)
}
@ -59,8 +59,8 @@ impl<'a> AnnotationFetcher<'a> for Store {
entry.get_internal_links()
.chain_err(|| AEK::StoreReadError)
.map(|iter| StoreIdIterator::new(Box::new(iter.map(|e| e.get_store_id().clone()))))
.map(|iter| NoteIterator::new(self, iter))
.map(|iter| AnnotationIter::new(iter))
.map(NoteIterator::new)
.map(|i| AnnotationIter::new(i, self))
}
}
@ -70,8 +70,9 @@ pub mod iter {
use toml_query::read::TomlValueReadExt;
use libimagnotes::note::Note;
use libimagnotes::note::NoteIterator;
use libimagnotes::iter::NoteIterator;
use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use error::Result;
use error::AnnotationErrorKind as AEK;
@ -79,23 +80,23 @@ pub mod iter {
use error::ResultExt;
#[derive(Debug)]
pub struct AnnotationIter<'a>(NoteIterator<'a>);
pub struct AnnotationIter<'a>(NoteIterator, &'a Store);
impl<'a> AnnotationIter<'a> {
pub fn new(noteiter: NoteIterator<'a>) -> AnnotationIter<'a> {
AnnotationIter(noteiter)
pub fn new(noteiter: NoteIterator, store: &'a Store) -> AnnotationIter<'a> {
AnnotationIter(noteiter, store)
}
}
impl<'a> Iterator for AnnotationIter<'a> {
type Item = Result<Note<'a>>;
type Item = Result<FileLockEntry<'a>>;
fn next(&mut self) -> Option<Result<Note<'a>>> {
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
Some(Ok(note)) => {
match self.0.next().map(|id| self.1.get(id)) {
Some(Ok(Some(note))) => {
match note.get_header().read("annotation.is_annotation") {
Ok(None) => continue, // not an annotation
Ok(Some(&Value::Boolean(true))) => return Some(Ok(note)),
@ -103,8 +104,9 @@ pub mod iter {
Err(e) => return Some(Err(e).chain_err(|| AEK::HeaderReadError)),
}
},
Some(Err(e)) => return Some(Err(e).chain_err(|| AEK::StoreReadError)),
None => return None, // iterator consumed
Some(Ok(None)) => continue,
Some(Err(e)) => return Some(Err(e).chain_err(|| AEK::StoreReadError)),
None => return None, // iterator consumed
}
}
}