Change the Task(FileLockEntry) type to a trait

This commit is contained in:
Mario Krehl 2017-09-02 12:23:29 +02:00
parent 1fe5a23331
commit f3bb6d02d0

View file

@ -18,8 +18,8 @@
// //
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::io::BufRead; use std::io::BufRead;
use std::marker::Sized;
use std::result::Result as RResult; use std::result::Result as RResult;
use toml::Value; use toml::Value;
@ -29,31 +29,43 @@ use task_hookrs::task::Task as TTask;
use task_hookrs::import::{import_task, import_tasks}; use task_hookrs::import::{import_task, import_tasks};
use libimagstore::store::{FileLockEntry, Store}; use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId}; use libimagstore::storeid::{IntoStoreId, StoreIdIterator};
use module_path::ModuleEntryPath; use module_path::ModuleEntryPath;
use error::{TodoErrorKind as TEK, MapErrInto}; use error::{TodoErrorKind as TEK, MapErrInto};
use result::Result; use result::Result;
/// Task struct containing a `FileLockEntry` /// Task struct containing a `FileLockEntry`
#[derive(Debug)] pub trait Task<'a> {
pub struct Task<'a>(FileLockEntry<'a>); fn import_task_from_reader<R: BufRead>(store: &'a Store, r: R) -> Result<(Self, String, Uuid)>
where Self: Sized;
fn get_task_from_import<R: BufRead>(store: &'a Store, r: R) -> Result<RResult<Self, String>>
where Self: Sized;
fn get_task_from_string(store: &'a Store, s: String) -> Result<RResult<Self, String>>
where Self: Sized;
fn get_task_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Option<Self>>
where Self: Sized;
fn retrieve_task_from_import<R: BufRead>(store: &'a Store, r: R) -> Result<Self>
where Self: Sized;
fn retrieve_task_from_string(store: &'a Store, s: String) -> Result<Self>
where Self: Sized;
fn delete_tasks_by_imports<R: BufRead>(store: &Store, r: R) -> Result<()>;
fn delete_task_by_uuid(store: &Store, uuid: Uuid) -> Result<()>;
fn all_tasks(store: &Store) -> Result<StoreIdIterator>;
fn new_from_twtask(store: &'a Store, task: TTask) -> Result<Self>
where Self: Sized;
}
impl<'a> Task<'a> { impl<'a> Task<'a> for FileLockEntry<'a> {
/// Concstructs a new `Task` with a `FileLockEntry` fn import_task_from_reader<R: BufRead>(store: &'a Store, mut r: R) -> Result<(FileLockEntry<'a>, String, Uuid)> {
pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
Task(fle)
}
pub fn import<R: BufRead>(store: &'a Store, mut r: R) -> Result<(Task<'a>, String, Uuid)> {
let mut line = String::new(); let mut line = String::new();
try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error)); try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error));
import_task(&line.as_str()) import_task(&line.as_str())
.map_err_into(TEK::ImportError) .map_err_into(TEK::ImportError)
.and_then(|t| { .and_then(|t| {
let uuid = t.uuid().clone(); let uuid = t.uuid().clone();
t.into_task(store).map(|t| (t, line, uuid)) Self::new_from_twtask(store, t).map(|t| (t, line, uuid))
}) })
} }
@ -66,23 +78,21 @@ impl<'a> Task<'a> {
/// * Ok(Err(String)) - where the String is the String read from the `r` parameter /// * 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 /// * 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>> fn get_task_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<RResult<FileLockEntry<'a>, String>> {
where R: BufRead
{
let mut line = String::new(); let mut line = String::new();
try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error)); try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error));
Task::get_from_string(store, line) Self::get_task_from_string(store, line)
} }
/// Get a task from a String. The String is expected to contain the JSON-representation of the /// 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) /// 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()`. /// 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>> { fn get_task_from_string(store: &'a Store, s: String) -> Result<RResult<FileLockEntry<'a>, String>> {
import_task(s.as_str()) import_task(s.as_str())
.map_err_into(TEK::ImportError) .map_err_into(TEK::ImportError)
.map(|t| t.uuid().clone()) .map(|t| t.uuid().clone())
.and_then(|uuid| Task::get_from_uuid(store, uuid)) .and_then(|uuid| Self::get_task_from_uuid(store, uuid))
.and_then(|o| match o { .and_then(|o| match o {
None => Ok(Err(s)), None => Ok(Err(s)),
Some(t) => Ok(Ok(t)), Some(t) => Ok(Ok(t)),
@ -92,35 +102,34 @@ impl<'a> Task<'a> {
/// Get a task from an UUID. /// Get a task from an UUID.
/// ///
/// If there is no task with this UUID, this returns `Ok(None)`. /// 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>>> { fn get_task_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Option<FileLockEntry<'a>>> {
ModuleEntryPath::new(format!("taskwarrior/{}", uuid)) ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
.into_storeid() .into_storeid()
.and_then(|store_id| store.get(store_id)) .and_then(|store_id| store.get(store_id))
.map(|o| o.map(Task::new))
.map_err_into(TEK::StoreError) .map_err_into(TEK::StoreError)
} }
/// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to /// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
/// implicitely create the task if it does not exist. /// 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>> { fn retrieve_task_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<FileLockEntry<'a>> {
let mut line = String::new(); let mut line = String::new();
try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error)); try!(r.read_line(&mut line).map_err_into(TEK::UTF8Error));
Task::retrieve_from_string(store, line) Self::retrieve_task_from_string(store, line)
} }
/// Retrieve a task from a String. The String is expected to contain the JSON-representation of /// 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) /// 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>> { fn retrieve_task_from_string(store: &'a Store, s: String) -> Result<FileLockEntry<'a>> {
Task::get_from_string(store, s) Self::get_task_from_string(store, s)
.and_then(|opt| match opt { .and_then(|opt| match opt {
Ok(task) => Ok(task), Ok(task) => Ok(task),
Err(string) => import_task(string.as_str()) Err(string) => import_task(string.as_str())
.map_err_into(TEK::ImportError) .map_err_into(TEK::ImportError)
.and_then(|t| t.into_task(store)), .and_then(|t| Self::new_from_twtask(store, t)),
}) })
} }
pub fn delete_by_imports<R: BufRead>(store: &Store, r: R) -> Result<()> { fn delete_tasks_by_imports<R: BufRead>(store: &Store, r: R) -> Result<()> {
use serde_json::ser::to_string as serde_to_string; use serde_json::ser::to_string as serde_to_string;
use task_hookrs::status::TaskStatus; use task_hookrs::status::TaskStatus;
@ -144,7 +153,7 @@ impl<'a> Task<'a> {
// Here we check if the status of a task is deleted and if yes, we delete it // Here we check if the status of a task is deleted and if yes, we delete it
// from the store. // from the store.
if *ttask.status() == TaskStatus::Deleted { if *ttask.status() == TaskStatus::Deleted {
match Task::delete_by_uuid(store, *ttask.uuid()) { match Self::delete_task_by_uuid(store, *ttask.uuid()) {
Ok(_) => info!("Deleted task {}", *ttask.uuid()), Ok(_) => info!("Deleted task {}", *ttask.uuid()),
Err(e) => return Err(e), Err(e) => return Err(e),
} }
@ -157,70 +166,23 @@ impl<'a> Task<'a> {
Ok(()) Ok(())
} }
pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> { fn delete_task_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
ModuleEntryPath::new(format!("taskwarrior/{}", uuid)) ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
.into_storeid() .into_storeid()
.and_then(|id| store.delete(id)) .and_then(|id| store.delete(id))
.map_err_into(TEK::StoreError) .map_err_into(TEK::StoreError)
} }
pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> { fn all_tasks(store: &Store) -> Result<StoreIdIterator> {
store.retrieve_for_module("todo/taskwarrior") store.retrieve_for_module("todo/taskwarrior")
.map_err_into(TEK::StoreError) .map_err_into(TEK::StoreError)
} }
pub fn all(store: &Store) -> Result<TaskIterator> { fn new_from_twtask(store: &'a Store, task: TTask) -> Result<FileLockEntry<'a>> {
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::read::TomlValueReadExt;
use toml_query::set::TomlValueSetExt; use toml_query::set::TomlValueSetExt;
let uuid = self.uuid(); let uuid = task.uuid();
ModuleEntryPath::new(format!("taskwarrior/{}", uuid)) ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
.into_storeid() .into_storeid()
.map_err_into(TEK::StoreIdError) .map_err_into(TEK::StoreIdError)
@ -241,47 +203,11 @@ impl<'a> IntoTask<'a> for TTask {
} }
// If none of the errors above have returned the function, everything is fine // If none of the errors above have returned the function, everything is fine
Ok(Task::new(fle)) Ok(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))
}
}