Merge pull request #1047 from mario-kr/all-extensions-as-traits_imag-todo
imag-todo/libimagtodo: All extensions as traits
This commit is contained in:
commit
6583ba04a2
5 changed files with 258 additions and 282 deletions
|
@ -35,7 +35,7 @@ use toml::Value;
|
||||||
|
|
||||||
use libimagrt::runtime::Runtime;
|
use libimagrt::runtime::Runtime;
|
||||||
use libimagrt::setup::generate_runtime_setup;
|
use libimagrt::setup::generate_runtime_setup;
|
||||||
use libimagtodo::task::Task;
|
use libimagtodo::taskstore::TaskStore;
|
||||||
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
|
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
|
@ -61,9 +61,11 @@ fn tw_hook(rt: &Runtime) {
|
||||||
let subcmd = rt.cli().subcommand_matches("tw-hook").unwrap();
|
let subcmd = rt.cli().subcommand_matches("tw-hook").unwrap();
|
||||||
if subcmd.is_present("add") {
|
if subcmd.is_present("add") {
|
||||||
let stdin = stdin();
|
let stdin = stdin();
|
||||||
let stdin = stdin.lock(); // implements BufRead which is required for `Task::import()`
|
|
||||||
|
|
||||||
match Task::import(rt.store(), stdin) {
|
// implements BufRead which is required for `Store::import_task_from_reader()`
|
||||||
|
let stdin = stdin.lock();
|
||||||
|
|
||||||
|
match rt.store().import_task_from_reader(stdin) {
|
||||||
Ok((_, line, uuid)) => println!("{}\nTask {} stored in imag", line, uuid),
|
Ok((_, line, uuid)) => println!("{}\nTask {} stored in imag", line, uuid),
|
||||||
Err(e) => trace_error_exit(&e, 1),
|
Err(e) => trace_error_exit(&e, 1),
|
||||||
}
|
}
|
||||||
|
@ -71,7 +73,7 @@ fn tw_hook(rt: &Runtime) {
|
||||||
// The used hook is "on-modify". This hook gives two json-objects
|
// The used hook is "on-modify". This hook gives two json-objects
|
||||||
// per usage und wants one (the second one) back.
|
// per usage und wants one (the second one) back.
|
||||||
let stdin = stdin();
|
let stdin = stdin();
|
||||||
Task::delete_by_imports(rt.store(), stdin.lock()).map_err_trace().ok();
|
rt.store().delete_tasks_by_imports(stdin.lock()).map_err_trace().ok();
|
||||||
} else {
|
} else {
|
||||||
// Should not be possible, as one argument is required via
|
// Should not be possible, as one argument is required via
|
||||||
// ArgGroup
|
// ArgGroup
|
||||||
|
@ -92,18 +94,21 @@ fn list(rt: &Runtime) {
|
||||||
is_match!(e.kind(), &::toml_query::error::ErrorKind::IdentifierNotFoundInDocument(_))
|
is_match!(e.kind(), &::toml_query::error::ErrorKind::IdentifierNotFoundInDocument(_))
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = Task::all(rt.store()) // get all tasks
|
let res = rt.store().all_tasks() // get all tasks
|
||||||
.map(|iter| { // and if this succeeded
|
.map(|iter| { // and if this succeeded
|
||||||
// filter out the ones were we can read the uuid
|
// filter out the ones were we can read the uuid
|
||||||
let uuids : Vec<_> = iter.filter_map(|t| match t {
|
let uuids : Vec<_> = iter.filter_map(|storeid| {
|
||||||
Ok(v) => match v.get_header().read(&String::from("todo.uuid")) {
|
match rt.store().retrieve(storeid) {
|
||||||
|
Ok(fle) => {
|
||||||
|
match fle.get_header().read(&String::from("todo.uuid")) {
|
||||||
Ok(Some(&Value::String(ref u))) => Some(u.clone()),
|
Ok(Some(&Value::String(ref u))) => Some(u.clone()),
|
||||||
Ok(Some(_)) => {
|
Ok(Some(_)) => {
|
||||||
warn!("Header type error");
|
error!("Header type error, expected String at 'todo.uuid' in {}",
|
||||||
|
fle.get_location());
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
warn!("Header missing field");
|
error!("Header missing field in {}", fle.get_location());
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -112,10 +117,12 @@ fn list(rt: &Runtime) {
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
trace_error(&e);
|
trace_error(&e);
|
||||||
None
|
None
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -23,7 +23,10 @@ generate_error_module!(
|
||||||
StoreError => "Store Error",
|
StoreError => "Store Error",
|
||||||
StoreIdError => "Store Id handling error",
|
StoreIdError => "Store Id handling error",
|
||||||
ImportError => "Error importing",
|
ImportError => "Error importing",
|
||||||
UTF8Error => "Encountered non-UTF8 characters while reading input"
|
UTF8Error => "Encountered non-UTF8 characters while reading input",
|
||||||
|
HeaderFieldMissing => "Header field missing",
|
||||||
|
HeaderTypeError => "Header field type error",
|
||||||
|
UuidParserError => "Uuid parser error"
|
||||||
);
|
);
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -48,4 +48,5 @@ module_entry_path_mod!("todo");
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod result;
|
pub mod result;
|
||||||
pub mod task;
|
pub mod task;
|
||||||
|
pub mod taskstore;
|
||||||
|
|
||||||
|
|
|
@ -17,271 +17,31 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
//
|
//
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use error::TodoErrorKind as TEK;
|
||||||
use std::ops::{Deref, DerefMut};
|
use error::MapErrInto;
|
||||||
use std::io::BufRead;
|
|
||||||
use std::result::Result as RResult;
|
|
||||||
|
|
||||||
use toml::Value;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use task_hookrs::task::Task as TTask;
|
|
||||||
use task_hookrs::import::{import_task, import_tasks};
|
|
||||||
|
|
||||||
use libimagstore::store::{FileLockEntry, Store};
|
|
||||||
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
|
|
||||||
use module_path::ModuleEntryPath;
|
|
||||||
|
|
||||||
use error::{TodoErrorKind as TEK, MapErrInto};
|
|
||||||
use result::Result;
|
use result::Result;
|
||||||
|
|
||||||
/// Task struct containing a `FileLockEntry`
|
use libimagstore::store::Entry;
|
||||||
#[derive(Debug)]
|
use libimagerror::into::IntoError;
|
||||||
pub struct Task<'a>(FileLockEntry<'a>);
|
|
||||||
|
|
||||||
impl<'a> Task<'a> {
|
use uuid::Uuid;
|
||||||
|
use toml::Value;
|
||||||
|
use toml_query::read::TomlValueReadExt;
|
||||||
|
|
||||||
/// Concstructs a new `Task` with a `FileLockEntry`
|
pub trait Task {
|
||||||
pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
|
fn get_uuid(&self) -> Result<Uuid>;
|
||||||
Task(fle)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn import<R: BufRead>(store: &'a Store, mut r: R) -> Result<(Task<'a>, String, Uuid)> {
|
impl Task for Entry {
|
||||||
let mut line = String::new();
|
fn get_uuid(&self) -> Result<Uuid> {
|
||||||
try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error));
|
match self.get_header().read("todo.uuid") {
|
||||||
import_task(&line.as_str())
|
Ok(Some(&Value::String(ref uuid))) => {
|
||||||
.map_err_into(TEK::ImportError)
|
Uuid::parse_str(uuid).map_err_into(TEK::UuidParserError)
|
||||||
.and_then(|t| {
|
|
||||||
let uuid = t.uuid().clone();
|
|
||||||
t.into_task(store).map(|t| (t, line, uuid))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a task from an import string. That is: read the imported string, get the UUID from it
|
|
||||||
/// and try to load this UUID from store.
|
|
||||||
///
|
|
||||||
/// Possible return values are:
|
|
||||||
///
|
|
||||||
/// * Ok(Ok(Task))
|
|
||||||
/// * Ok(Err(String)) - where the String is the String read from the `r` parameter
|
|
||||||
/// * Err(_) - where the error is an error that happened during evaluation
|
|
||||||
///
|
|
||||||
pub fn get_from_import<R>(store: &'a Store, mut r: R) -> Result<RResult<Task<'a>, String>>
|
|
||||||
where R: BufRead
|
|
||||||
{
|
|
||||||
let mut line = String::new();
|
|
||||||
try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error));
|
|
||||||
Task::get_from_string(store, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a task from a String. The String is expected to contain the JSON-representation of the
|
|
||||||
/// Task to get from the store (only the UUID really matters in this case)
|
|
||||||
///
|
|
||||||
/// For an explanation on the return values see `Task::get_from_import()`.
|
|
||||||
pub fn get_from_string(store: &'a Store, s: String) -> Result<RResult<Task<'a>, String>> {
|
|
||||||
import_task(s.as_str())
|
|
||||||
.map_err_into(TEK::ImportError)
|
|
||||||
.map(|t| t.uuid().clone())
|
|
||||||
.and_then(|uuid| Task::get_from_uuid(store, uuid))
|
|
||||||
.and_then(|o| match o {
|
|
||||||
None => Ok(Err(s)),
|
|
||||||
Some(t) => Ok(Ok(t)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a task from an UUID.
|
|
||||||
///
|
|
||||||
/// If there is no task with this UUID, this returns `Ok(None)`.
|
|
||||||
pub fn get_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Option<Task<'a>>> {
|
|
||||||
ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
|
|
||||||
.into_storeid()
|
|
||||||
.and_then(|store_id| store.get(store_id))
|
|
||||||
.map(|o| o.map(Task::new))
|
|
||||||
.map_err_into(TEK::StoreError)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
|
|
||||||
/// implicitely create the task if it does not exist.
|
|
||||||
pub fn retrieve_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<Task<'a>> {
|
|
||||||
let mut line = String::new();
|
|
||||||
try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error));
|
|
||||||
Task::retrieve_from_string(store, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve a task from a String. The String is expected to contain the JSON-representation of
|
|
||||||
/// the Task to retrieve from the store (only the UUID really matters in this case)
|
|
||||||
pub fn retrieve_from_string(store: &'a Store, s: String) -> Result<Task<'a>> {
|
|
||||||
Task::get_from_string(store, s)
|
|
||||||
.and_then(|opt| match opt {
|
|
||||||
Ok(task) => Ok(task),
|
|
||||||
Err(string) => import_task(string.as_str())
|
|
||||||
.map_err_into(TEK::ImportError)
|
|
||||||
.and_then(|t| t.into_task(store)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete_by_imports<R: BufRead>(store: &Store, r: R) -> Result<()> {
|
|
||||||
use serde_json::ser::to_string as serde_to_string;
|
|
||||||
use task_hookrs::status::TaskStatus;
|
|
||||||
|
|
||||||
for (counter, res_ttask) in import_tasks(r).into_iter().enumerate() {
|
|
||||||
match res_ttask {
|
|
||||||
Ok(ttask) => {
|
|
||||||
if counter % 2 == 1 {
|
|
||||||
// Only every second task is needed, the first one is the
|
|
||||||
// task before the change, and the second one after
|
|
||||||
// the change. The (maybe modified) second one is
|
|
||||||
// expected by taskwarrior.
|
|
||||||
match serde_to_string(&ttask).map_err_into(TEK::ImportError) {
|
|
||||||
// use println!() here, as we talk with TW
|
|
||||||
Ok(val) => println!("{}", val),
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Taskwarrior does not have the concept of deleted tasks, but only modified
|
|
||||||
// ones.
|
|
||||||
//
|
|
||||||
// Here we check if the status of a task is deleted and if yes, we delete it
|
|
||||||
// from the store.
|
|
||||||
if *ttask.status() == TaskStatus::Deleted {
|
|
||||||
match Task::delete_by_uuid(store, *ttask.uuid()) {
|
|
||||||
Ok(_) => info!("Deleted task {}", *ttask.uuid()),
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // end if c % 2
|
|
||||||
},
|
},
|
||||||
Err(e) => return Err(e).map_err_into(TEK::ImportError),
|
Ok(Some(_)) => Err(TEK::HeaderTypeError.into_error()),
|
||||||
|
Ok(None) => Err(TEK::HeaderFieldMissing.into_error()),
|
||||||
|
Err(e) => Err(e).map_err_into(TEK::StoreError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
|
|
||||||
ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
|
|
||||||
.into_storeid()
|
|
||||||
.and_then(|id| store.delete(id))
|
|
||||||
.map_err_into(TEK::StoreError)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> {
|
|
||||||
store.retrieve_for_module("todo/taskwarrior")
|
|
||||||
.map_err_into(TEK::StoreError)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all(store: &Store) -> Result<TaskIterator> {
|
|
||||||
Task::all_as_ids(store)
|
|
||||||
.map(|iter| TaskIterator::new(store, iter))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Deref for Task<'a> {
|
|
||||||
type Target = FileLockEntry<'a>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &FileLockEntry<'a> {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DerefMut for Task<'a> {
|
|
||||||
|
|
||||||
fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
|
|
||||||
pub trait IntoTask<'a> {
|
|
||||||
|
|
||||||
/// # Usage
|
|
||||||
/// ```ignore
|
|
||||||
/// use std::io::stdin;
|
|
||||||
///
|
|
||||||
/// use task_hookrs::task::Task;
|
|
||||||
/// use task_hookrs::import::import;
|
|
||||||
/// use libimagstore::store::{Store, FileLockEntry};
|
|
||||||
///
|
|
||||||
/// if let Ok(task_hookrs_task) = import(stdin()) {
|
|
||||||
/// // Store is given at runtime
|
|
||||||
/// let task = task_hookrs_task.into_filelockentry(store);
|
|
||||||
/// println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
fn into_task(self, store : &'a Store) -> Result<Task<'a>>;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoTask<'a> for TTask {
|
|
||||||
|
|
||||||
fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
|
|
||||||
use toml_query::read::TomlValueReadExt;
|
|
||||||
use toml_query::set::TomlValueSetExt;
|
|
||||||
|
|
||||||
let uuid = self.uuid();
|
|
||||||
ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
|
|
||||||
.into_storeid()
|
|
||||||
.map_err_into(TEK::StoreIdError)
|
|
||||||
.and_then(|id| {
|
|
||||||
store.retrieve(id)
|
|
||||||
.map_err_into(TEK::StoreError)
|
|
||||||
.and_then(|mut fle| {
|
|
||||||
{
|
|
||||||
let hdr = fle.get_header_mut();
|
|
||||||
if try!(hdr.read("todo").map_err_into(TEK::StoreError)).is_none() {
|
|
||||||
try!(hdr
|
|
||||||
.set("todo", Value::Table(BTreeMap::new()))
|
|
||||||
.map_err_into(TEK::StoreError));
|
|
||||||
}
|
|
||||||
|
|
||||||
try!(hdr.set("todo.uuid", Value::String(format!("{}",uuid)))
|
|
||||||
.map_err_into(TEK::StoreError));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If none of the errors above have returned the function, everything is fine
|
|
||||||
Ok(Task::new(fle))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
trait FromStoreId {
|
|
||||||
fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromStoreId for Task<'a> {
|
|
||||||
|
|
||||||
fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
|
|
||||||
store.retrieve(id)
|
|
||||||
.map_err_into(TEK::StoreError)
|
|
||||||
.map(Task::new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TaskIterator<'a> {
|
|
||||||
store: &'a Store,
|
|
||||||
iditer: StoreIdIterator,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TaskIterator<'a> {
|
|
||||||
|
|
||||||
pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
|
|
||||||
TaskIterator {
|
|
||||||
store: store,
|
|
||||||
iditer: iditer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for TaskIterator<'a> {
|
|
||||||
type Item = Result<Task<'a>>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Result<Task<'a>>> {
|
|
||||||
self.iditer.next().map(|id| Task::from_storeid(self.store, id))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
205
lib/domain/libimagtodo/src/taskstore.rs
Normal file
205
lib/domain/libimagtodo/src/taskstore.rs
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
//
|
||||||
|
// 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 std::io::BufRead;
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
|
||||||
|
use toml::Value;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use task_hookrs::task::Task as TTask;
|
||||||
|
use task_hookrs::import::{import_task, import_tasks};
|
||||||
|
|
||||||
|
use libimagstore::store::{FileLockEntry, Store};
|
||||||
|
use libimagstore::storeid::{IntoStoreId, StoreIdIterator};
|
||||||
|
use module_path::ModuleEntryPath;
|
||||||
|
|
||||||
|
use error::{TodoErrorKind as TEK, MapErrInto};
|
||||||
|
use result::Result;
|
||||||
|
|
||||||
|
/// Task struct containing a `FileLockEntry`
|
||||||
|
pub trait TaskStore<'a> {
|
||||||
|
fn import_task_from_reader<R: BufRead>(&'a self, r: R) -> Result<(FileLockEntry<'a>, String, Uuid)>;
|
||||||
|
fn get_task_from_import<R: BufRead>(&'a self, r: R) -> Result<RResult<FileLockEntry<'a>, String>>;
|
||||||
|
fn get_task_from_string(&'a self, s: String) -> Result<RResult<FileLockEntry<'a>, String>>;
|
||||||
|
fn get_task_from_uuid(&'a self, uuid: Uuid) -> Result<Option<FileLockEntry<'a>>>;
|
||||||
|
fn retrieve_task_from_import<R: BufRead>(&'a self, r: R) -> Result<FileLockEntry<'a>>;
|
||||||
|
fn retrieve_task_from_string(&'a self, s: String) -> Result<FileLockEntry<'a>>;
|
||||||
|
fn delete_tasks_by_imports<R: BufRead>(&self, r: R) -> Result<()>;
|
||||||
|
fn delete_task_by_uuid(&self, uuid: Uuid) -> Result<()>;
|
||||||
|
fn all_tasks(&self) -> Result<StoreIdIterator>;
|
||||||
|
fn new_from_twtask(&'a self, task: TTask) -> Result<FileLockEntry<'a>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TaskStore<'a> for Store {
|
||||||
|
|
||||||
|
fn import_task_from_reader<R: BufRead>(&'a self, mut r: R) -> Result<(FileLockEntry<'a>, String, Uuid)> {
|
||||||
|
let mut line = String::new();
|
||||||
|
try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error));
|
||||||
|
import_task(&line.as_str())
|
||||||
|
.map_err_into(TEK::ImportError)
|
||||||
|
.and_then(|t| {
|
||||||
|
let uuid = t.uuid().clone();
|
||||||
|
self.new_from_twtask(t).map(|t| (t, line, uuid))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a task from an import string. That is: read the imported string, get the UUID from it
|
||||||
|
/// and try to load this UUID from store.
|
||||||
|
///
|
||||||
|
/// Possible return values are:
|
||||||
|
///
|
||||||
|
/// * Ok(Ok(Task))
|
||||||
|
/// * Ok(Err(String)) - where the String is the String read from the `r` parameter
|
||||||
|
/// * Err(_) - where the error is an error that happened during evaluation
|
||||||
|
///
|
||||||
|
fn get_task_from_import<R: BufRead>(&'a self, mut r: R) -> Result<RResult<FileLockEntry<'a>, String>> {
|
||||||
|
let mut line = String::new();
|
||||||
|
try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error));
|
||||||
|
self.get_task_from_string(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a task from a String. The String is expected to contain the JSON-representation of the
|
||||||
|
/// Task to get from the store (only the UUID really matters in this case)
|
||||||
|
///
|
||||||
|
/// For an explanation on the return values see `Task::get_from_import()`.
|
||||||
|
fn get_task_from_string(&'a self, s: String) -> Result<RResult<FileLockEntry<'a>, String>> {
|
||||||
|
import_task(s.as_str())
|
||||||
|
.map_err_into(TEK::ImportError)
|
||||||
|
.map(|t| t.uuid().clone())
|
||||||
|
.and_then(|uuid| self.get_task_from_uuid(uuid))
|
||||||
|
.and_then(|o| match o {
|
||||||
|
None => Ok(Err(s)),
|
||||||
|
Some(t) => Ok(Ok(t)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a task from an UUID.
|
||||||
|
///
|
||||||
|
/// If there is no task with this UUID, this returns `Ok(None)`.
|
||||||
|
fn get_task_from_uuid(&'a self, uuid: Uuid) -> Result<Option<FileLockEntry<'a>>> {
|
||||||
|
ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
|
||||||
|
.into_storeid()
|
||||||
|
.and_then(|store_id| self.get(store_id))
|
||||||
|
.map_err_into(TEK::StoreError)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
|
||||||
|
/// implicitely create the task if it does not exist.
|
||||||
|
fn retrieve_task_from_import<R: BufRead>(&'a self, mut r: R) -> Result<FileLockEntry<'a>> {
|
||||||
|
let mut line = String::new();
|
||||||
|
try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error));
|
||||||
|
self.retrieve_task_from_string(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a task from a String. The String is expected to contain the JSON-representation of
|
||||||
|
/// the Task to retrieve from the store (only the UUID really matters in this case)
|
||||||
|
fn retrieve_task_from_string(&'a self, s: String) -> Result<FileLockEntry<'a>> {
|
||||||
|
self.get_task_from_string(s)
|
||||||
|
.and_then(|opt| match opt {
|
||||||
|
Ok(task) => Ok(task),
|
||||||
|
Err(string) => import_task(string.as_str())
|
||||||
|
.map_err_into(TEK::ImportError)
|
||||||
|
.and_then(|t| self.new_from_twtask(t)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_tasks_by_imports<R: BufRead>(&self, r: R) -> Result<()> {
|
||||||
|
use serde_json::ser::to_string as serde_to_string;
|
||||||
|
use task_hookrs::status::TaskStatus;
|
||||||
|
|
||||||
|
for (counter, res_ttask) in import_tasks(r).into_iter().enumerate() {
|
||||||
|
match res_ttask {
|
||||||
|
Ok(ttask) => {
|
||||||
|
if counter % 2 == 1 {
|
||||||
|
// Only every second task is needed, the first one is the
|
||||||
|
// task before the change, and the second one after
|
||||||
|
// the change. The (maybe modified) second one is
|
||||||
|
// expected by taskwarrior.
|
||||||
|
match serde_to_string(&ttask).map_err_into(TEK::ImportError) {
|
||||||
|
// use println!() here, as we talk with TW
|
||||||
|
Ok(val) => println!("{}", val),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taskwarrior does not have the concept of deleted tasks, but only modified
|
||||||
|
// ones.
|
||||||
|
//
|
||||||
|
// Here we check if the status of a task is deleted and if yes, we delete it
|
||||||
|
// from the store.
|
||||||
|
if *ttask.status() == TaskStatus::Deleted {
|
||||||
|
match self.delete_task_by_uuid(*ttask.uuid()) {
|
||||||
|
Ok(_) => info!("Deleted task {}", *ttask.uuid()),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end if c % 2
|
||||||
|
},
|
||||||
|
Err(e) => return Err(e).map_err_into(TEK::ImportError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_task_by_uuid(&self, uuid: Uuid) -> Result<()> {
|
||||||
|
ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
|
||||||
|
.into_storeid()
|
||||||
|
.and_then(|id| self.delete(id))
|
||||||
|
.map_err_into(TEK::StoreError)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_tasks(&self) -> Result<StoreIdIterator> {
|
||||||
|
self.retrieve_for_module("todo/taskwarrior")
|
||||||
|
.map_err_into(TEK::StoreError)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_from_twtask(&'a self, task: TTask) -> Result<FileLockEntry<'a>> {
|
||||||
|
use toml_query::read::TomlValueReadExt;
|
||||||
|
use toml_query::set::TomlValueSetExt;
|
||||||
|
|
||||||
|
let uuid = task.uuid();
|
||||||
|
ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
|
||||||
|
.into_storeid()
|
||||||
|
.map_err_into(TEK::StoreIdError)
|
||||||
|
.and_then(|id| {
|
||||||
|
self.retrieve(id)
|
||||||
|
.map_err_into(TEK::StoreError)
|
||||||
|
.and_then(|mut fle| {
|
||||||
|
{
|
||||||
|
let hdr = fle.get_header_mut();
|
||||||
|
if try!(hdr.read("todo").map_err_into(TEK::StoreError)).is_none() {
|
||||||
|
try!(hdr
|
||||||
|
.set("todo", Value::Table(BTreeMap::new()))
|
||||||
|
.map_err_into(TEK::StoreError));
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(hdr.set("todo.uuid", Value::String(format!("{}",uuid)))
|
||||||
|
.map_err_into(TEK::StoreError));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none of the errors above have returned the function, everything is fine
|
||||||
|
Ok(fle)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue