diff --git a/imag-todo/src/main.rs b/imag-todo/src/main.rs index 945724b7..b0d533b1 100644 --- a/imag-todo/src/main.rs +++ b/imag-todo/src/main.rs @@ -16,210 +16,147 @@ extern crate libimagtodo; use std::process::exit; use std::process::{Command, Stdio}; use std::io::stdin; -use std::io::BufRead; -use task_hookrs::import::{import_task, import_tasks}; +use toml::Value; + +use task_hookrs::import::import_tasks; +use task_hookrs::status::TaskStatus; use libimagrt::runtime::Runtime; -use libimagtodo::task::IntoTask; +use libimagrt::setup::generate_runtime_setup; +use libimagtodo::task::Task; use libimagerror::trace::trace_error; mod ui; use ui::build_ui; fn main() { + let rt = generate_runtime_setup("imag-todo", + &version!()[..], + "Interface with taskwarrior", + build_ui); - let name = "imag-todo"; - let version = &version!()[..]; - let about = "Interface with taskwarrior"; - let ui = build_ui(Runtime::get_default_cli_builder(name, version, about)); - - let rt = { - let rt = Runtime::new(ui); - if rt.is_ok() { - rt.unwrap() - } else { - println!("Could not set up Runtime"); - println!("{:?}", rt.unwrap_err()); - exit(1); - } - }; - - - - let scmd = rt.cli().subcommand_name(); - match scmd { - Some("tw-hook") => { - let subcmd = rt.cli().subcommand_matches("tw-hook").unwrap(); - if subcmd.is_present("add") { - let stdin = stdin(); - let mut stdin = stdin.lock(); - let mut line = String::new(); - match stdin.read_line(&mut line) { - Ok(_) => { } - Err(e) => { - error!("{}", e); - return; - } - }; - if let Ok(ttask) = import_task(&line.as_str()) { - let uuid = *ttask.uuid(); - println!("{}", match serde_json::ser::to_string(&ttask) { - Ok(val) => val, - Err(e) => { - error!("{}", e); - return; - } - }); - match ttask.into_filelockentry(rt.store()) { - Ok(val) => { - println!("Task {} stored in imag", uuid); - val - }, - Err(e) => { - trace_error(&e); - error!("{}", e); - return; - } - }; - } - else { - error!("No usable input"); - return; - } - } - else if subcmd.is_present("delete") { - // The used hook is "on-modify". This hook gives two json-objects - // per usage und wants one (the second one) back. - let mut counter = 0; - let stdin = stdin(); - let stdin = stdin.lock(); - if let Ok(ttasks) = import_tasks(stdin) { - for ttask in ttasks { - 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. - println!("{}", match serde_json::ser::to_string(&ttask) { - Ok(val) => val, - Err(e) => { - error!("{}", e); - return; - } - }); - match ttask.status() { - &task_hookrs::status::TaskStatus::Deleted => { - match libimagtodo::delete::delete(rt.store(), *ttask.uuid()) { - Ok(_) => { - println!("Deleted task {}", *ttask.uuid()); - } - Err(e) => { - trace_error(&e); - error!("{}", e); - return; - } - } - } - _ => { - } - } // end match ttask.status() - } // end if c % 2 - counter += 1; - } // end for - } // end if let - else { - error!("No usable input"); - } - } - else { - // Should not be possible, as one argument is required via - // ArgGroup - unreachable!(); - } - }, - Some("exec") => { - let subcmd = rt.cli().subcommand_matches("exec").unwrap(); - let mut args = Vec::new(); - if let Some(exec_string) = subcmd.values_of("command") { - for e in exec_string { - args.push(e); - } - let tw_process = Command::new("task").stdin(Stdio::null()).args(&args).spawn().unwrap_or_else(|e| { - panic!("failed to execute taskwarrior: {}", e); - }); - - let output = tw_process.wait_with_output().unwrap_or_else(|e| { - panic!("failed to unwrap output: {}", e); - }); - let outstring = String::from_utf8(output.stdout).unwrap_or_else(|e| { - panic!("failed to ececute: {}", e); - }); - println!("{}", outstring); - } else { - panic!("faild to execute: You need to exec --command"); - } - } - Some("list") => { - let subcmd = rt.cli().subcommand_matches("list").unwrap(); - let mut args = Vec::new(); - let verbose = subcmd.is_present("verbose"); - let iter = match libimagtodo::read::get_todo_iterator(rt.store()) { - //let iter = match rt.store().retrieve_for_module("todo/taskwarrior") { - Err(e) => { - error!("{}", e); - return; - } - Ok(val) => val, - }; - for task in iter { - match task { - Ok(val) => { - //let val = libimagtodo::task::Task::new(fle); - //println!("{:#?}", val.flentry); - let uuid = match val.flentry.get_header().read("todo.uuid") { - Ok(Some(u)) => u, - Ok(None) => continue, - Err(e) => { - error!("{}", e); - continue; - } - }; - if verbose { - args.clear(); - args.push(format!("uuid:{}", uuid)); - args.push(format!("{}", "information")); - let tw_process = Command::new("task").stdin(Stdio::null()).args(&args).spawn() - .unwrap_or_else(|e| { - error!("{}", e); - panic!("failed"); - }); - let output = tw_process.wait_with_output().unwrap_or_else(|e| { - panic!("failed to unwrap output: {}", e); - }); - let outstring = String::from_utf8(output.stdout).unwrap_or_else(|e| { - panic!("failed to ececute: {}", e); - }); - println!("{}", outstring); - } - else { - println!("{}", match uuid { - toml::Value::String(s) => s, - _ => { - error!("Unexpected type for todo.uuid: {}", uuid); - continue; - }, - }); - } - } - Err(e) => { - error!("{}", e); - continue; - } - } // end match task - } // end for - } - _ => unimplemented!(), + match rt.cli().subcommand_name() { + Some("tw-hook") => tw_hook(&rt), + Some("list") => list(&rt), + _ => unreachable!(), } // end match scmd } // end main +fn tw_hook(rt: &Runtime) { + let subcmd = rt.cli().subcommand_matches("tw-hook").unwrap(); + if subcmd.is_present("add") { + let stdin = stdin(); + let stdin = stdin.lock(); // implements BufRead which is required for `Task::import()` + + match Task::import(rt.store(), stdin) { + Ok((_, line, uuid)) => info!("{}\nTask {} stored in imag", line, uuid), + Err(e) => { + trace_error(&e); + exit(1); + } + } + } else if subcmd.is_present("delete") { + // The used hook is "on-modify". This hook gives two json-objects + // per usage und wants one (the second one) back. + let stdin = stdin(); + let stdin = stdin.lock(); + + match import_tasks(stdin) { + Ok(ttasks) => for (counter, ttask) in ttasks.iter().enumerate() { + 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_json::ser::to_string(&ttask) { + Ok(val) => println!("{}", val), + Err(e) => { + trace_error(&e); + exit(1); + } + } + + // 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(rt.store(), *ttask.uuid()) { + Ok(_) => println!("Deleted task {}", *ttask.uuid()), + Err(e) => { + trace_error(&e); + exit(1); + } + } + } + } // end if c % 2 + }, + Err(e) => { + trace_error(&e); + exit(1); + }, + } + } else { + // Should not be possible, as one argument is required via + // ArgGroup + unreachable!(); + } +} + +fn list(rt: &Runtime) { + let subcmd = rt.cli().subcommand_matches("list").unwrap(); + let verbose = subcmd.is_present("verbose"); + + let res = Task::all(rt.store()) // get all tasks + .map(|iter| { // and if this succeeded + // filter out the ones were we can read the uuid + let uuids : Vec<_> = iter.filter_map(|t| match t { + Ok(v) => match v.get_header().read("todo.uuid") { + Ok(Some(Value::String(ref u))) => Some(u.clone()), + Ok(Some(_)) => { + warn!("Header type error"); + None + }, + Ok(None) => None, + Err(e) => { + trace_error(&e); + None + } + }, + Err(e) => { + trace_error(&e); + None + } + }) + .collect(); + + // compose a `task` call with them, ... + let outstring = if verbose { // ... if verbose + let output = Command::new("task") + .stdin(Stdio::null()) + .args(&uuids) + .spawn() + .unwrap_or_else(|e| { + trace_error(&e); + panic!("Failed to execute `task` on the commandline. I'm dying now."); + }) + .wait_with_output() + .unwrap_or_else(|e| panic!("failed to unwrap output: {}", e)); + + String::from_utf8(output.stdout) + .unwrap_or_else(|e| panic!("failed to execute: {}", e)) + } else { // ... else just join them + uuids.join("\n") + }; + + /// and then print that + println!("{}", outstring); + }); + + if let Err(e) = res { + trace_error(&e); + } +} + diff --git a/imag-todo/src/ui.rs b/imag-todo/src/ui.rs index ae2adc07..f781673e 100644 --- a/imag-todo/src/ui.rs +++ b/imag-todo/src/ui.rs @@ -27,73 +27,16 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .required(true)) ) - .subcommand(SubCommand::with_name("exec") - .about("Send a command to taskwarrior") - .version("0.1") + .subcommand(SubCommand::with_name("list") + .about("List all tasks") + .version("0.1") - .arg(Arg::with_name("command") - .long("command") - .short("c") - .takes_value(true) - .multiple(true) - .required(true) - .help("Args written in the string will be send directly to taskwarrior") - ) - ) - - .subcommand(SubCommand::with_name("add") - .about("create a task") - .version("0.1") - - .arg(Arg::with_name("description") - .long("description") - .short("d") - .takes_value(true) - .required(true) - .help("Name/Description of the new task") - ) - - .arg(Arg::with_name("priority") - .long("priority") - .short("p") - .takes_value(true) - .required(false) - .help("One of l, m, h for low, medium and high priority") - ) - - .arg(Arg::with_name("project") - .long("project") - .takes_value(true) - .required(false) - .help("Name of the project the task is related to") - ) - - .arg(Arg::with_name("due") - .long("due") - .takes_value(true) - .required(false) - .help("Due date of the new task") - ) - - .arg(Arg::with_name("frequency") - .long("frequency") - .short("f") - .takes_value(true) - .required(false) - .help("Frequency of the recurrence of a task") - ) - ) - - .subcommand(SubCommand::with_name("list") - .about("List all tasks") - .version("0.1") - - .arg(Arg::with_name("verbose") - .long("verbose") - .short("v") - .takes_value(false) - .required(false) - .help("Asks taskwarrior for all the details") - ) - ) + .arg(Arg::with_name("verbose") + .long("verbose") + .short("v") + .takes_value(false) + .required(false) + .help("Asks taskwarrior for all the details") + ) + ) } diff --git a/libimagtodo/Cargo.toml b/libimagtodo/Cargo.toml index b6582ac4..a3b652c5 100644 --- a/libimagtodo/Cargo.toml +++ b/libimagtodo/Cargo.toml @@ -11,3 +11,7 @@ toml = "0.1.28" [dependencies.libimagstore] path = "../libimagstore" + +[dependencies.libimagerror] +path = "../libimagerror" + diff --git a/libimagtodo/src/delete.rs b/libimagtodo/src/delete.rs deleted file mode 100644 index ed56dc0c..00000000 --- a/libimagtodo/src/delete.rs +++ /dev/null @@ -1,19 +0,0 @@ -use uuid::Uuid; - -use libimagstore::store::Store; -use libimagstore::storeid::IntoStoreId; -use module_path::ModuleEntryPath; - -use error::{TodoError, TodoErrorKind}; - -/// With the uuid we get the storeid and then we can delete the entry -pub fn delete(store : &Store, uuid: Uuid) -> Result<(),TodoError> { - // With the uuid we get the storeid - let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid(); - // It deletes an entry - match store.delete(store_id) { - Ok(val) => Ok(val), - Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))), - } -} - diff --git a/libimagtodo/src/error.rs b/libimagtodo/src/error.rs index 58fe7758..8aa42410 100644 --- a/libimagtodo/src/error.rs +++ b/libimagtodo/src/error.rs @@ -1,66 +1,12 @@ -use std::error::Error; -use std::clone::Clone; -use std::fmt::Error as FmtError; -use std::fmt::{Display, Formatter}; +generate_error_module!( + generate_error_types!(TodoError, TodoErrorKind, + ConversionError => "Conversion Error", + StoreError => "Store Error", + ImportError => "Error importing" + ); +); -/// Enum of Error Types, as of now we have two: -/// * ConversionError: for Errors concerning conversion failures from task_hookrs::task::Task to -/// libimagtodo::task::Task. unused. -/// * StoreError: For Errors thrown by functions of the Store/structs relates to the Store -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum TodoErrorKind { - ConversionError, - StoreError, -} +pub use self::error::TodoError; +pub use self::error::TodoErrorKind; +pub use self::error::MapErrInto; -/// Maps a TodoErrorKind to a String -fn todo_error_type_as_str(e: &TodoErrorKind) -> &'static str { - match e { - &TodoErrorKind::ConversionError => "Conversion Error", - &TodoErrorKind::StoreError => "Store Error", - } -} - -impl Display for TodoErrorKind { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { - try!(write!(fmt, "{}", todo_error_type_as_str(self))); - Ok(()) - } -} - -/// Error struct for the imag-todo module -#[derive(Debug)] -pub struct TodoError { - err_type : TodoErrorKind, - cause : Option>, -} - -impl TodoError { - /// Creates a new TodoError, with TodoErrorKind errtype and an optional cause - pub fn new(errtype : TodoErrorKind, cause : Option>) -> TodoError { - TodoError { - err_type : errtype, - cause : cause, - } - } - /// Returns the error type (TodoErrorKind) - pub fn err_type(&self) -> TodoErrorKind { - self.err_type.clone() - } -} - -impl Display for TodoError { - fn fmt(&self, fmt : &mut Formatter) -> Result<(), FmtError> { - try!(write!(fmt, "[{}]", todo_error_type_as_str(&self.err_type.clone()))); - Ok(()) - } -} - -impl Error for TodoError { - fn description(&self) -> &str { - todo_error_type_as_str(&self.err_type.clone()) - } - fn cause(&self) -> Option<&Error> { - self.cause.as_ref().map(|e| &**e) - } -} diff --git a/libimagtodo/src/lib.rs b/libimagtodo/src/lib.rs index 0396704a..10966aba 100644 --- a/libimagtodo/src/lib.rs +++ b/libimagtodo/src/lib.rs @@ -3,13 +3,12 @@ extern crate uuid; extern crate toml; #[macro_use] extern crate libimagstore; +#[macro_use] extern crate libimagerror; extern crate task_hookrs; module_entry_path_mod!("todo", "0.1.0"); -pub mod delete; pub mod error; -pub mod read; pub mod result; pub mod task; diff --git a/libimagtodo/src/read.rs b/libimagtodo/src/read.rs deleted file mode 100644 index 57c326c9..00000000 --- a/libimagtodo/src/read.rs +++ /dev/null @@ -1,51 +0,0 @@ -use libimagstore::storeid::{StoreIdIterator, StoreId}; -use libimagstore::store::Store; -use error::{TodoError, TodoErrorKind}; -use task::Task; -use result::Result; - -pub fn get_todo_iterator(store: &Store) -> Result { - - store.retrieve_for_module("todo/taskwarrior") - .map(|iter| TaskIterator::new(store, iter)) - .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) -} - -trait FromStoreId { - fn from_storeid<'a>(&'a Store, StoreId) -> Result>; -} - -impl<'a> FromStoreId for Task<'a> { - - fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result> { - match store.retrieve(id) { - Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))), - Ok(c) => Ok(Task::new( c )), - } - } -} - -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>; - - fn next(&mut self) -> Option>> { - self.iditer - .next() - .map(|id| Task::from_storeid(self.store, id)) - } -} diff --git a/libimagtodo/src/task.rs b/libimagtodo/src/task.rs index ba9c7e9e..89db1b71 100644 --- a/libimagtodo/src/task.rs +++ b/libimagtodo/src/task.rs @@ -1,34 +1,142 @@ use std::collections::BTreeMap; +use std::ops::{Deref, DerefMut}; +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; +use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId}; use module_path::ModuleEntryPath; -use error::{TodoError, TodoErrorKind}; +use error::{TodoError, TodoErrorKind, MapErrInto}; use result::Result; /// Task struct containing a `FileLockEntry` #[derive(Debug)] -pub struct Task<'a> { - pub flentry : FileLockEntry<'a>, -} +pub struct Task<'a>(FileLockEntry<'a>); impl<'a> Task<'a> { + /// Concstructs a new `Task` with a `FileLockEntry` - pub fn new(fle : FileLockEntry<'a>) -> Task<'a> { - Task { - flentry : fle - } + pub fn new(fle: FileLockEntry<'a>) -> Task<'a> { + Task(fle) } + + pub fn import(store: &'a Store, mut r: R) -> Result<(Task<'a>, String, Uuid)> { + let mut line = String::new(); + r.read_line(&mut line); + import_task(&line.as_str()) + .map_err_into(TodoErrorKind::ImportError) + .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(store: &'a Store, mut r: R) -> Result, String>> + { + let mut line = String::new(); + r.read_line(&mut line); + 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, String>> { + import_task(s.as_str()) + .map_err_into(TodoErrorKind::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>> { + let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid(); + + store.get(store_id) + .map(|o| o.map(Task::new)) + .map_err_into(TodoErrorKind::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(store: &'a Store, mut r: R) -> Result> { + let mut line = String::new(); + r.read_line(&mut line); + 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::get_from_string(store, s) + .and_then(|opt| match opt { + Ok(task) => Ok(task), + Err(string) => import_task(string.as_str()) + .map_err_into(TodoErrorKind::ImportError) + .and_then(|t| t.into_task(store)), + }) + } + + pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> { + store.delete(ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid()) + .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) + } + + pub fn all_as_ids(store: &Store) -> Result { + store.retrieve_for_module("todo/taskwarrior") + .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) + } + + pub fn all(store: &Store) -> Result { + 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. -/// This Task struct is merely a wrapper for a `FileLockEntry`, therefore the function name -/// `into_filelockentry`. pub trait IntoTask<'a> { + /// # Usage /// ```ignore /// use std::io::stdin; @@ -43,26 +151,25 @@ pub trait IntoTask<'a> { /// println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid")); /// } /// ``` - fn into_filelockentry(self, store : &'a Store) -> Result>; + fn into_task(self, store : &'a Store) -> Result>; + } + impl<'a> IntoTask<'a> for TTask { - fn into_filelockentry(self, store : &'a Store) -> Result> { - let uuid = self.uuid(); + + fn into_task(self, store : &'a Store) -> Result> { + let uuid = self.uuid(); let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid(); + match store.retrieve(store_id) { - Err(e) => { - return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) - }, + Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))), Ok(mut fle) => { { let mut header = fle.get_header_mut(); match header.read("todo") { Ok(None) => { - match header.set("todo", Value::Table(BTreeMap::new())) { - Ok(_) => { }, - Err(e) => { - return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) - } + if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) { + return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) } } Ok(Some(_)) => { } @@ -70,16 +177,55 @@ impl<'a> IntoTask<'a> for TTask { return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) } } - match header.set("todo.uuid", Value::String(format!("{}",uuid))) { - Ok(_) => { }, - Err(e) => { - return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) - } + + if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) { + return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) } } + // If none of the errors above have returned the function, everything is fine - Ok(Task { flentry : fle } ) + Ok(Task::new(fle)) } } } + } + +trait FromStoreId { + fn from_storeid<'a>(&'a Store, StoreId) -> Result>; +} + +impl<'a> FromStoreId for Task<'a> { + + fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result> { + match store.retrieve(id) { + Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))), + Ok(c) => Ok(Task::new( c )), + } + } +} + +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>; + + fn next(&mut self) -> Option>> { + self.iditer.next().map(|id| Task::from_storeid(self.store, id)) + } +} +