Merge pull request #13 from matthiasbeyer/libimagtodo/init
Cleanup patches for libimagtodo/imag-todo
This commit is contained in:
commit
9bceef573b
8 changed files with 331 additions and 426 deletions
|
@ -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!(),
|
||||||
|
|
||||||
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!(),
|
|
||||||
} // end match scmd
|
} // end match scmd
|
||||||
} // end main
|
} // 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,73 +27,16 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
.required(true))
|
.required(true))
|
||||||
)
|
)
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("exec")
|
.subcommand(SubCommand::with_name("list")
|
||||||
.about("Send a command to taskwarrior")
|
.about("List all tasks")
|
||||||
.version("0.1")
|
.version("0.1")
|
||||||
|
|
||||||
.arg(Arg::with_name("command")
|
.arg(Arg::with_name("verbose")
|
||||||
.long("command")
|
.long("verbose")
|
||||||
.short("c")
|
.short("v")
|
||||||
.takes_value(true)
|
.takes_value(false)
|
||||||
.multiple(true)
|
.required(false)
|
||||||
.required(true)
|
.help("Asks taskwarrior for all the details")
|
||||||
.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")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,3 +11,7 @@ toml = "0.1.28"
|
||||||
|
|
||||||
[dependencies.libimagstore]
|
[dependencies.libimagstore]
|
||||||
path = "../libimagstore"
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
|
|
@ -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)))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,26 +151,25 @@ 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>> {
|
|
||||||
let uuid = self.uuid();
|
fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
|
||||||
|
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(_) => { },
|
return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
|
||||||
Err(e) => {
|
|
||||||
return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(_)) => { }
|
Ok(Some(_)) => { }
|
||||||
|
@ -70,16 +177,55 @@ impl<'a> IntoTask<'a> for TTask {
|
||||||
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue