Merge pull request #13 from matthiasbeyer/libimagtodo/init

Cleanup patches for libimagtodo/imag-todo
This commit is contained in:
mario-kr 2016-08-06 11:05:39 +02:00 committed by GitHub
commit 9bceef573b
8 changed files with 331 additions and 426 deletions

View file

@ -16,210 +16,147 @@ extern crate libimagtodo;
use std::process::exit; use std::process::exit;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::io::stdin; 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 libimagrt::runtime::Runtime;
use libimagtodo::task::IntoTask; use libimagrt::setup::generate_runtime_setup;
use libimagtodo::task::Task;
use libimagerror::trace::trace_error; use libimagerror::trace::trace_error;
mod ui; mod ui;
use ui::build_ui; use ui::build_ui;
fn main() { fn main() {
let rt = generate_runtime_setup("imag-todo",
&version!()[..],
"Interface with taskwarrior",
build_ui);
let name = "imag-todo"; match rt.cli().subcommand_name() {
let version = &version!()[..]; Some("tw-hook") => tw_hook(&rt),
let about = "Interface with taskwarrior"; Some("list") => list(&rt),
let ui = build_ui(Runtime::get_default_cli_builder(name, version, about)); _ => unreachable!(),
} // end match scmd
} // end main
let rt = { fn tw_hook(rt: &Runtime) {
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(); 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 mut stdin = stdin.lock(); let stdin = stdin.lock(); // implements BufRead which is required for `Task::import()`
let mut line = String::new();
match stdin.read_line(&mut line) { match Task::import(rt.store(), stdin) {
Ok(_) => { } Ok((_, line, uuid)) => info!("{}\nTask {} stored in imag", line, uuid),
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) => { Err(e) => {
trace_error(&e); trace_error(&e);
error!("{}", e); exit(1);
return;
}
};
}
else {
error!("No usable input");
return;
} }
} }
else if subcmd.is_present("delete") { } else if subcmd.is_present("delete") {
// 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 mut counter = 0;
let stdin = stdin(); let stdin = stdin();
let stdin = stdin.lock(); let stdin = stdin.lock();
if let Ok(ttasks) = import_tasks(stdin) {
for ttask in ttasks { match import_tasks(stdin) {
Ok(ttasks) => for (counter, ttask) in ttasks.iter().enumerate() {
if counter % 2 == 1 { if counter % 2 == 1 {
// Only every second task is needed, the first one is the // Only every second task is needed, the first one is the
// task before the change, and the second one after // task before the change, and the second one after
// the change. The (maybe modified) second one is // the change. The (maybe modified) second one is
// expected by taskwarrior. // expected by taskwarrior.
println!("{}", match serde_json::ser::to_string(&ttask) { match serde_json::ser::to_string(&ttask) {
Ok(val) => val, Ok(val) => println!("{}", 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) => { Err(e) => {
trace_error(&e); trace_error(&e);
error!("{}", e); exit(1);
return; }
}
// 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 match ttask.status()
} // end if c % 2 } // end if c % 2
counter += 1; },
} // end for Err(e) => {
} // end if let trace_error(&e);
else { exit(1);
error!("No usable input"); },
} }
} } else {
else {
// Should not be possible, as one argument is required via // Should not be possible, as one argument is required via
// ArgGroup // ArgGroup
unreachable!(); 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| { fn list(rt: &Runtime) {
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 subcmd = rt.cli().subcommand_matches("list").unwrap();
let mut args = Vec::new();
let verbose = subcmd.is_present("verbose"); 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") { let res = Task::all(rt.store()) // get all tasks
Err(e) => { .map(|iter| { // and if this succeeded
error!("{}", e); // filter out the ones were we can read the uuid
return; let uuids : Vec<_> = iter.filter_map(|t| match t {
} Ok(v) => match v.get_header().read("todo.uuid") {
Ok(val) => val, Ok(Some(Value::String(ref u))) => Some(u.clone()),
}; Ok(Some(_)) => {
for task in iter { warn!("Header type error");
match task { None
Ok(val) => { },
//let val = libimagtodo::task::Task::new(fle); Ok(None) => None,
//println!("{:#?}", val.flentry); Err(e) => {
let uuid = match val.flentry.get_header().read("todo.uuid") { trace_error(&e);
Ok(Some(u)) => u, None
Ok(None) => continue, }
Err(e) => { },
error!("{}", e); Err(e) => {
continue; trace_error(&e);
} None
}; }
if verbose { })
args.clear(); .collect();
args.push(format!("uuid:{}", uuid));
args.push(format!("{}", "information")); // compose a `task` call with them, ...
let tw_process = Command::new("task").stdin(Stdio::null()).args(&args).spawn() let outstring = if verbose { // ... if verbose
.unwrap_or_else(|e| { let output = Command::new("task")
error!("{}", e); .stdin(Stdio::null())
panic!("failed"); .args(&uuids)
}); .spawn()
let output = tw_process.wait_with_output().unwrap_or_else(|e| { .unwrap_or_else(|e| {
panic!("failed to unwrap output: {}", e); trace_error(&e);
}); panic!("Failed to execute `task` on the commandline. I'm dying now.");
let outstring = String::from_utf8(output.stdout).unwrap_or_else(|e| { })
panic!("failed to ececute: {}", e); .wait_with_output()
}); .unwrap_or_else(|e| panic!("failed to unwrap output: {}", e));
println!("{}", outstring);
} String::from_utf8(output.stdout)
else { .unwrap_or_else(|e| panic!("failed to execute: {}", e))
println!("{}", match uuid { } else { // ... else just join them
toml::Value::String(s) => s, uuids.join("\n")
_ => { };
error!("Unexpected type for todo.uuid: {}", uuid);
continue; /// and then print that
}, println!("{}", outstring);
}); });
}
} if let Err(e) = res {
Err(e) => { trace_error(&e);
error!("{}", e); }
continue; }
}
} // end match task
} // end for
}
_ => unimplemented!(),
} // end match scmd
} // end main

View file

@ -27,63 +27,6 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
.required(true)) .required(true))
) )
.subcommand(SubCommand::with_name("exec")
.about("Send a command to taskwarrior")
.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") .subcommand(SubCommand::with_name("list")
.about("List all tasks") .about("List all tasks")
.version("0.1") .version("0.1")

View file

@ -11,3 +11,7 @@ toml = "0.1.28"
[dependencies.libimagstore] [dependencies.libimagstore]
path = "../libimagstore" path = "../libimagstore"
[dependencies.libimagerror]
path = "../libimagerror"

View file

@ -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)))),
}
}

View file

@ -1,66 +1,12 @@
use std::error::Error; generate_error_module!(
use std::clone::Clone; generate_error_types!(TodoError, TodoErrorKind,
use std::fmt::Error as FmtError; ConversionError => "Conversion Error",
use std::fmt::{Display, Formatter}; StoreError => "Store Error",
ImportError => "Error importing"
);
);
/// Enum of Error Types, as of now we have two: pub use self::error::TodoError;
/// * ConversionError: for Errors concerning conversion failures from task_hookrs::task::Task to pub use self::error::TodoErrorKind;
/// libimagtodo::task::Task. unused. pub use self::error::MapErrInto;
/// * StoreError: For Errors thrown by functions of the Store/structs relates to the Store
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TodoErrorKind {
ConversionError,
StoreError,
}
/// 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<Box<Error>>,
}
impl TodoError {
/// Creates a new TodoError, with TodoErrorKind errtype and an optional cause
pub fn new(errtype : TodoErrorKind, cause : Option<Box<Error>>) -> 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)
}
}

View file

@ -3,13 +3,12 @@ extern crate uuid;
extern crate toml; extern crate toml;
#[macro_use] extern crate libimagstore; #[macro_use] extern crate libimagstore;
#[macro_use] extern crate libimagerror;
extern crate task_hookrs; extern crate task_hookrs;
module_entry_path_mod!("todo", "0.1.0"); module_entry_path_mod!("todo", "0.1.0");
pub mod delete;
pub mod error; pub mod error;
pub mod read;
pub mod result; pub mod result;
pub mod task; pub mod task;

View file

@ -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<TaskIterator> {
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<Task<'a>>;
}
impl<'a> FromStoreId for Task<'a> {
fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
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<Task<'a>>;
fn next(&mut self) -> Option<Result<Task<'a>>> {
self.iditer
.next()
.map(|id| Task::from_storeid(self.store, id))
}
}

View file

@ -1,34 +1,142 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::io::BufRead;
use std::result::Result as RResult;
use toml::Value; use toml::Value;
use uuid::Uuid;
use task_hookrs::task::Task as TTask; use task_hookrs::task::Task as TTask;
use task_hookrs::import::{import_task, import_tasks};
use libimagstore::store::{FileLockEntry, Store}; use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::IntoStoreId; use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
use module_path::ModuleEntryPath; use module_path::ModuleEntryPath;
use error::{TodoError, TodoErrorKind}; use error::{TodoError, TodoErrorKind, MapErrInto};
use result::Result; use result::Result;
/// Task struct containing a `FileLockEntry` /// Task struct containing a `FileLockEntry`
#[derive(Debug)] #[derive(Debug)]
pub struct Task<'a> { pub struct Task<'a>(FileLockEntry<'a>);
pub flentry : FileLockEntry<'a>,
}
impl<'a> Task<'a> { impl<'a> Task<'a> {
/// Concstructs a new `Task` with a `FileLockEntry` /// Concstructs a new `Task` with a `FileLockEntry`
pub fn new(fle : FileLockEntry<'a>) -> Task<'a> { pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
Task { Task(fle)
flentry : fle
} }
pub fn import<R: BufRead>(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<R: BufRead>(store: &'a Store, mut r: R) -> Result<RResult<Task<'a>, 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<RResult<Task<'a>, 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<Option<Task<'a>>> {
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<R: BufRead>(store: &'a Store, mut r: R) -> Result<Task<'a>> {
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<'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(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<StoreIdIterator> {
store.retrieve_for_module("todo/taskwarrior")
.map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
}
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. /// 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> { pub trait IntoTask<'a> {
/// # Usage /// # Usage
/// ```ignore /// ```ignore
/// use std::io::stdin; /// use std::io::stdin;
@ -43,43 +151,81 @@ pub trait IntoTask<'a> {
/// println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid")); /// println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
/// } /// }
/// ``` /// ```
fn into_filelockentry(self, store : &'a Store) -> Result<Task<'a>>; fn into_task(self, store : &'a Store) -> Result<Task<'a>>;
} }
impl<'a> IntoTask<'a> for TTask { impl<'a> IntoTask<'a> for TTask {
fn into_filelockentry(self, store : &'a Store) -> Result<Task<'a>> {
fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
let uuid = self.uuid(); let uuid = self.uuid();
let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid(); let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();
match store.retrieve(store_id) { match store.retrieve(store_id) {
Err(e) => { Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
},
Ok(mut fle) => { Ok(mut fle) => {
{ {
let mut header = fle.get_header_mut(); let mut header = fle.get_header_mut();
match header.read("todo") { match header.read("todo") {
Ok(None) => { Ok(None) => {
match header.set("todo", Value::Table(BTreeMap::new())) { if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) {
Ok(_) => { },
Err(e) => {
return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
} }
} }
}
Ok(Some(_)) => { } Ok(Some(_)) => { }
Err(e) => { Err(e) => {
return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
} }
} }
match header.set("todo.uuid", Value::String(format!("{}",uuid))) {
Ok(_) => { }, if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) {
Err(e) => {
return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))) return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
} }
} }
}
// 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 { flentry : fle } ) 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>> {
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<Task<'a>>;
fn next(&mut self) -> Option<Result<Task<'a>>> {
self.iditer.next().map(|id| Task::from_storeid(self.store, id))
}
}