diff --git a/bin/core/imag-annotate/src/main.rs b/bin/core/imag-annotate/src/main.rs index 639488ea..5beb0c1b 100644 --- a/bin/core/imag-annotate/src/main.rs +++ b/bin/core/imag-annotate/src/main.rs @@ -45,10 +45,8 @@ extern crate libimagstore; extern crate libimagutil; use std::io::Write; -use std::path::PathBuf; use failure::Error; -use failure::err_msg; use libimagentryannotation::annotateable::*; use libimagentryannotation::annotation_fetcher::*; @@ -56,10 +54,10 @@ use libimagentryedit::edit::*; use libimagerror::trace::MapErrTrace; use libimagerror::exit::ExitUnwrap; use libimagerror::io::ToExitCode; +use libimagerror::errors::ErrorMsg as EM; use libimagrt::runtime::Runtime; use libimagrt::setup::generate_runtime_setup; use libimagstore::store::FileLockEntry; -use libimagstore::storeid::IntoStoreId; mod ui; @@ -91,89 +89,96 @@ fn main() { fn add(rt: &Runtime) { let scmd = rt.cli().subcommand_matches("add").unwrap(); // safed by main() let annotation_name = scmd.value_of("annotation_name").unwrap(); // safed by clap - let entry_name = scmd - .value_of("entry") - .map(PathBuf::from) - .map(|pb| pb.into_storeid().map_err_trace_exit_unwrap(1)) - .unwrap(); // safed by clap + let ids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1); + + ids.into_iter().for_each(|id| { + let _ = rt.store() + .get(id.clone()) + .map_err_trace_exit_unwrap(1) + .ok_or_else(|| EM::EntryNotFound(id.local_display_string())) + .map_err(Error::from) + .map_err_trace_exit_unwrap(1) + .annotate(rt.store(), annotation_name) + .map_err_trace_exit_unwrap(1) + .edit_content(&rt) + .map_err_trace_exit_unwrap(1); + }) - let _ = rt.store() - .get(entry_name) - .map_err_trace_exit_unwrap(1) - .ok_or_else(|| Error::from(err_msg("Entry does not exist".to_owned()))) - .map_err_trace_exit_unwrap(1) - .annotate(rt.store(), annotation_name) - .map_err_trace_exit_unwrap(1) - .edit_content(&rt) - .map_err_trace_exit_unwrap(1); } fn remove(rt: &Runtime) { let scmd = rt.cli().subcommand_matches("remove").unwrap(); // safed by main() - let entry_name = scmd.value_of("entry").unwrap(); // safed by clap let annotation_name = scmd.value_of("annotation_name").unwrap(); // safed by clap let delete = scmd.is_present("delete-annotation"); + let ids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1); - let mut entry = rt.store() - .get(PathBuf::from(entry_name).into_storeid().map_err_trace_exit_unwrap(1)) - .map_err_trace_exit_unwrap(1) - .ok_or_else(|| Error::from(err_msg("Entry does not exist".to_owned()))) - .map_err_trace_exit_unwrap(1); + ids.into_iter().for_each(|id| { + let mut entry = rt.store() + .get(id.clone()) + .map_err_trace_exit_unwrap(1) + .ok_or_else(|| EM::EntryNotFound(id.local_display_string())) + .map_err(Error::from) + .map_err_trace_exit_unwrap(1); - let annotation = entry - .denotate(rt.store(), annotation_name) - .map_err_trace_exit_unwrap(1); + let annotation = entry + .denotate(rt.store(), annotation_name) + .map_err_trace_exit_unwrap(1); - if delete { - debug!("Deleting annotation object"); - if let Some(an) = annotation { - let loc = an.get_location().clone(); - drop(an); + if delete { + debug!("Deleting annotation object"); + if let Some(an) = annotation { + let loc = an.get_location().clone(); + drop(an); - let _ = rt - .store() - .delete(loc) - .map_err_trace_exit_unwrap(1); + let _ = rt + .store() + .delete(loc) + .map_err_trace_exit_unwrap(1); + } else { + warn!("Not having annotation object, cannot delete!"); + } } else { - warn!("Not having annotation object, cannot delete!"); + debug!("Not deleting annotation object"); } - } else { - debug!("Not deleting annotation object"); - } + }) + } fn list(rt: &Runtime) { - let scmd = rt.cli().subcommand_matches("list").unwrap(); // safed by clap - let with_text = scmd.is_present("list-with-text"); - match scmd.value_of("entry").map(PathBuf::from) { - Some(pb) => { - let _ = rt - .store() - .get(pb.into_storeid().map_err_trace_exit_unwrap(1)) - .map_err_trace_exit_unwrap(1) - .ok_or_else(|| Error::from(err_msg("Entry does not exist"))) - .map_err_trace_exit_unwrap(1) - .annotations(rt.store()) - .map_err_trace_exit_unwrap(1) - .enumerate() - .map(|(i, a)| { - list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text) - }) - .collect::>(); - } + let scmd = rt.cli().subcommand_matches("list").unwrap(); // safed by clap + let with_text = scmd.is_present("list-with-text"); + let ids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1); - None => { - // show them all - let _ = rt - .store() - .all_annotations() - .map_err_trace_exit_unwrap(1) - .enumerate() - .map(|(i, a)| { - list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text) - }) - .collect::>(); - } + if ids.len() != 0 { + let _ = ids + .into_iter() + .for_each(|id| { + let _ = rt + .store() + .get(id.clone()) + .map_err_trace_exit_unwrap(1) + .ok_or_else(|| EM::EntryNotFound(id.local_display_string())) + .map_err(Error::from) + .map_err_trace_exit_unwrap(1) + .annotations(rt.store()) + .map_err_trace_exit_unwrap(1) + .enumerate() + .map(|(i, a)| { + list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text) + }) + .collect::>(); + }); + } else { // ids.len() == 0 + // show them all + let _ = rt + .store() + .all_annotations() + .map_err_trace_exit_unwrap(1) + .enumerate() + .map(|(i, a)| { + list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text) + }) + .collect::>(); } } diff --git a/bin/core/imag-annotate/src/ui.rs b/bin/core/imag-annotate/src/ui.rs index 544cd2a4..1cf119e6 100644 --- a/bin/core/imag-annotate/src/ui.rs +++ b/bin/core/imag-annotate/src/ui.rs @@ -17,7 +17,14 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, App, SubCommand}; +use std::path::PathBuf; + +use clap::{Arg, ArgMatches, App, SubCommand}; + +use libimagstore::storeid::StoreId; +use libimagstore::storeid::IntoStoreId; +use libimagrt::runtime::IdPathProvider; +use libimagerror::trace::MapErrTrace; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -86,3 +93,56 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { ) } +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(matches: &ArgMatches) -> Vec { + match matches.subcommand() { + ("add", Some(subm)) => { + subm.values_of("entry") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1) + }, + + ("remove", Some(subm)) => { + subm.values_of("entry") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1) + }, + + ("list", Some(subm)) => { + subm.values_of("entry") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1) + }, + + (other, _) => { + error!("Not a known command: {}", other); + ::std::process::exit(1) + } + } + } +} diff --git a/bin/core/imag-category/src/main.rs b/bin/core/imag-category/src/main.rs index c44d0a26..e13d49e3 100644 --- a/bin/core/imag-category/src/main.rs +++ b/bin/core/imag-category/src/main.rs @@ -47,13 +47,10 @@ use libimagerror::exit::ExitUnwrap; use libimagerror::io::ToExitCode; use libimagrt::runtime::Runtime; use libimagrt::setup::generate_runtime_setup; -use libimagstore::storeid::IntoStoreId; mod ui; use std::io::Write; -use std::io::Read; -use std::path::PathBuf; use libimagentrycategory::store::CategoryStore; use libimagstore::storeid::StoreIdIterator; @@ -93,29 +90,7 @@ fn main() { fn set(rt: &Runtime) { let scmd = rt.cli().subcommand_matches("set").unwrap(); // safed by main() let name = scmd.value_of("set-name").map(String::from).unwrap(); // safed by clap - let sids = match scmd.value_of("set-ids") { - Some(path) => vec![PathBuf::from(path).into_storeid().map_err_trace_exit_unwrap(1)], - None => if rt.cli().is_present("entries-from-stdin") { - let stdin = rt.stdin().unwrap_or_else(|| { - error!("Cannot get handle to stdin"); - ::std::process::exit(1) - }); - - let mut buf = String::new(); - let _ = stdin.lock().read_to_string(&mut buf).unwrap_or_else(|_| { - error!("Failed to read from stdin"); - ::std::process::exit(1) - }); - - buf.lines() - .map(PathBuf::from) - .map(|p| p.into_storeid().map_err_trace_exit_unwrap(1)) - .collect() - } else { - error!("Something weird happened. I was not able to find the path of the entries to edit"); - ::std::process::exit(1) - } - }; + let sids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1); StoreIdIterator::new(Box::new(sids.into_iter().map(Ok))) .into_get_iter(rt.store()) @@ -132,31 +107,7 @@ fn set(rt: &Runtime) { } fn get(rt: &Runtime) { - let scmd = rt.cli().subcommand_matches("get").unwrap(); // safed by main() - let sids = match scmd.value_of("get-ids") { - Some(path) => vec![PathBuf::from(path).into_storeid().map_err_trace_exit_unwrap(1)], - None => if rt.cli().is_present("entries-from-stdin") { - let stdin = rt.stdin().unwrap_or_else(|| { - error!("Cannot get handle to stdin"); - ::std::process::exit(1) - }); - - let mut buf = String::new(); - let _ = stdin.lock().read_to_string(&mut buf).unwrap_or_else(|_| { - error!("Failed to read from stdin"); - ::std::process::exit(1) - }); - - buf.lines() - .map(PathBuf::from) - .map(|p| p.into_storeid().map_err_trace_exit_unwrap(1)) - .collect() - } else { - error!("Something weird happened. I was not able to find the path of the entries to edit"); - ::std::process::exit(1) - } - }; - + let sids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1); let out = rt.stdout(); let mut outlock = out.lock(); diff --git a/bin/core/imag-category/src/ui.rs b/bin/core/imag-category/src/ui.rs index 6d32711c..62f8b144 100644 --- a/bin/core/imag-category/src/ui.rs +++ b/bin/core/imag-category/src/ui.rs @@ -17,7 +17,14 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, ArgGroup, App, SubCommand}; +use std::path::PathBuf; + +use clap::{Arg, ArgMatches, App, SubCommand}; + +use libimagstore::storeid::StoreId; +use libimagstore::storeid::IntoStoreId; +use libimagrt::runtime::IdPathProvider; +use libimagerror::trace::MapErrTrace; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -79,17 +86,6 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .multiple(true) .help("The entries to set the category for") .value_name("ID")) - .arg(Arg::with_name("entries-from-stdin") - .long("ids-from-stdin") - .short("I") - .takes_value(false) - .required(false) - .multiple(false) - .help("Read the ids for the entries from stdin")) - - .group(ArgGroup::with_name("input-method") - .args(&["set-ids", "entries-from-stdin"]) - .required(true)) ) .subcommand(SubCommand::with_name("get") @@ -102,17 +98,65 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .multiple(true) .help("The id of the Entry to get the category for") .value_name("ID")) - .arg(Arg::with_name("entries-from-stdin") - .long("ids-from-stdin") - .short("I") - .takes_value(false) - .required(false) - .multiple(false) - .help("Read the ids for the entries from stdin")) - - .group(ArgGroup::with_name("input-method") - .args(&["get-ids", "entries-from-stdin"]) - .required(true)) ) } +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(matches: &ArgMatches) -> Vec { + match matches.subcommand() { + ("create-category", _) => { + error!("Command does not get IDs as input"); + ::std::process::exit(1) + }, + + ("delete-category", _) => { + error!("Command does not get IDs as input"); + ::std::process::exit(1) + }, + + ("list-categories", _) => { + error!("Command does not get IDs as input"); + ::std::process::exit(1) + }, + + ("list-category", _) => { + error!("Command does not get IDs as input"); + ::std::process::exit(1) + }, + + ("set", Some(subm)) => { + subm.values_of("set-ids") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1) + }, + + ("get", Some(subm)) => { + subm.values_of("get-ids") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1) + }, + + (other, _) => { + error!("Not a known command: {}", other); + ::std::process::exit(1) + } + } + } +} diff --git a/bin/core/imag-edit/src/main.rs b/bin/core/imag-edit/src/main.rs index 46c4c3f9..6d4ac384 100644 --- a/bin/core/imag-edit/src/main.rs +++ b/bin/core/imag-edit/src/main.rs @@ -41,15 +41,11 @@ extern crate libimagerror; extern crate libimagstore; extern crate libimagutil; -use std::path::PathBuf; -use std::io::Read; - use libimagerror::trace::MapErrTrace; use libimagerror::iter::TraceIterator; use libimagentryedit::edit::Edit; use libimagentryedit::edit::EditHeader; use libimagrt::setup::generate_runtime_setup; -use libimagstore::storeid::IntoStoreId; use libimagstore::storeid::StoreIdIterator; use libimagstore::iter::get::StoreIdGetIteratorExtension; @@ -62,33 +58,11 @@ fn main() { "Edit store entries with $EDITOR", ui::build_ui); - let sids = match rt.cli().value_of("entry") { - Some(path) => vec![PathBuf::from(path).into_storeid().map_err_trace_exit_unwrap(1)], - None => if rt.cli().is_present("entries-from-stdin") { - let stdin = rt.stdin().unwrap_or_else(|| { - error!("Cannot get handle to stdin"); - ::std::process::exit(1) - }); - - let mut buf = String::new(); - let _ = stdin.lock().read_to_string(&mut buf).unwrap_or_else(|_| { - error!("Failed to read from stdin"); - ::std::process::exit(1) - }); - - buf.lines() - .map(PathBuf::from) - .map(|p| p.into_storeid().map_err_trace_exit_unwrap(1)) - .collect() - } else { - error!("Something weird happened. I was not able to find the path of the entries to edit"); - ::std::process::exit(1) - } - }; - let edit_header = rt.cli().is_present("edit-header"); let edit_header_only = rt.cli().is_present("edit-header-only"); + let sids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1); + StoreIdIterator::new(Box::new(sids.into_iter().map(Ok))) .into_get_iter(rt.store()) .trace_unwrap_exit(1) diff --git a/bin/core/imag-edit/src/ui.rs b/bin/core/imag-edit/src/ui.rs index 2f3e8f66..fbb1d5e3 100644 --- a/bin/core/imag-edit/src/ui.rs +++ b/bin/core/imag-edit/src/ui.rs @@ -17,7 +17,14 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, ArgGroup, App}; +use std::path::PathBuf; + +use clap::{Arg, ArgMatches, App}; + +use libimagstore::storeid::IntoStoreId; +use libimagstore::storeid::StoreId; +use libimagrt::runtime::IdPathProvider; +use libimagerror::trace::MapErrTrace; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -28,16 +35,6 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .multiple(true) .help("The entry/entries to edit") .value_name("ENTRY")) - .arg(Arg::with_name("entries-from-stdin") - .long("ids-from-stdin") - .short("I") - .takes_value(false) - .required(false) - .multiple(false) - .help("The entry/entries are piped in via stdin")) - .group(ArgGroup::with_name("input-method") - .args(&["entry", "entries-from-stdin"]) - .required(true)) .arg(Arg::with_name("edit-header") .long("header") @@ -55,3 +52,20 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .help("Only edit the header")) } +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(matches: &ArgMatches) -> Vec { + matches + .values_of("entry") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1) + } +} diff --git a/bin/core/imag-gps/src/main.rs b/bin/core/imag-gps/src/main.rs index abf121ac..50c94f61 100644 --- a/bin/core/imag-gps/src/main.rs +++ b/bin/core/imag-gps/src/main.rs @@ -45,7 +45,6 @@ extern crate libimagstore; use std::io::Write; use std::process::exit; -use std::path::PathBuf; use std::str::FromStr; use failure::Error; @@ -58,7 +57,6 @@ use libimagrt::runtime::Runtime; use libimagerror::trace::MapErrTrace; use libimagerror::exit::ExitUnwrap; use libimagerror::io::ToExitCode; -use libimagstore::storeid::IntoStoreId; mod ui; @@ -88,13 +86,6 @@ fn main() { } fn add(rt: &Runtime) { - let scmd = rt.cli().subcommand_matches("add").unwrap(); // safed by main() - - let entry_name = scmd.value_of("entry").unwrap(); // safed by clap - let sid = PathBuf::from(entry_name) - .into_storeid() - .map_err_trace_exit_unwrap(1); - let c = { let parse = |value: &str| -> (i64, i64, i64) { debug!("Parsing '{}' into degree, minute and second", value); @@ -120,6 +111,8 @@ fn add(rt: &Runtime) { (*degree, *minute, *second) }; + let scmd = rt.cli().subcommand_matches("add").unwrap(); // safed by main() + let long = parse(scmd.value_of("longitude").unwrap()); // unwrap safed by clap let lati = parse(scmd.value_of("latitude").unwrap()); // unwrap safed by clap @@ -129,70 +122,90 @@ fn add(rt: &Runtime) { Coordinates::new(long, lati) }; - rt.store() - .get(sid) + rt.ids::<::ui::PathProvider>() .map_err_trace_exit_unwrap(1) - .map(|mut entry| { - let _ = entry.set_coordinates(c).map_err_trace_exit_unwrap(1); - }) - .unwrap_or_else(|| { - error!("No such entry: {}", entry_name); - exit(1) + .into_iter() + .for_each(|id| { + rt.store() + .get(id.clone()) + .map_err_trace_exit_unwrap(1) + .unwrap_or_else(|| { // if we have Ok(None) + error!("No such entry: {}", id); + exit(1) + }) + .set_coordinates(c.clone()) + .map_err_trace_exit_unwrap(1); + + let _ = rt + .report_touched(&id) + .map_err_trace_exit_unwrap(1); }); } fn remove(rt: &Runtime) { - let scmd = rt.cli().subcommand_matches("remove").unwrap(); // safed by main() + let print_removed = rt + .cli() + .subcommand_matches("remove") + .unwrap() + .is_present("print-removed"); // safed by main() - let entry_name = scmd.value_of("entry").unwrap(); // safed by clap - let sid = PathBuf::from(entry_name) - .into_storeid() - .map_err_trace_exit_unwrap(1); - - let removed_value = rt - .store() - .get(sid) + rt.ids::<::ui::PathProvider>() .map_err_trace_exit_unwrap(1) - .unwrap_or_else(|| { // if we have Ok(None) - error!("No such entry: {}", entry_name); - exit(1) - }) - .remove_coordinates() - .map_err_trace_exit_unwrap(1) // The delete action failed - .unwrap_or_else(|| { // if we have Ok(None) - error!("Entry had no coordinates: {}", entry_name); - exit(1) - }) - .map_err_trace_exit_unwrap(1); // The parsing of the deleted values failed + .into_iter() + .for_each(|id| { + let removed_value = rt + .store() + .get(id.clone()) + .map_err_trace_exit_unwrap(1) + .unwrap_or_else(|| { // if we have Ok(None) + error!("No such entry: {}", id); + exit(1) + }) + .remove_coordinates() + .map_err_trace_exit_unwrap(1) // The delete action failed + .unwrap_or_else(|| { // if we have Ok(None) + error!("Entry had no coordinates: {}", id); + exit(1) + }) + .map_err_trace_exit_unwrap(1); // The parsing of the deleted values failed - if scmd.is_present("print-removed") { - let _ = writeln!(rt.stdout(), "{}", removed_value).to_exit_code().unwrap_or_exit(); - } + if print_removed { + let _ = writeln!(rt.stdout(), "{}", removed_value).to_exit_code().unwrap_or_exit(); + } + + let _ = rt + .report_touched(&id) + .map_err_trace_exit_unwrap(1); + }); } fn get(rt: &Runtime) { - let scmd = rt.cli().subcommand_matches("get").unwrap(); // safed by main() - - let entry_name = scmd.value_of("entry").unwrap(); // safed by clap - let sid = PathBuf::from(entry_name) - .into_storeid() - .map_err_trace_exit_unwrap(1); - - let value = rt - .store() - .get(sid) + let mut stdout = rt.stdout(); + rt.ids::<::ui::PathProvider>() .map_err_trace_exit_unwrap(1) - .unwrap_or_else(|| { // if we have Ok(None) - error!("No such entry: {}", entry_name); - exit(1) - }) - .get_coordinates() - .map_err_trace_exit_unwrap(1) // The get action failed - .unwrap_or_else(|| { // if we have Ok(None) - error!("Entry has no coordinates: {}", entry_name); - exit(1) - }); + .into_iter() + .for_each(|id| { + let value = rt + .store() + .get(id.clone()) + .map_err_trace_exit_unwrap(1) + .unwrap_or_else(|| { // if we have Ok(None) + error!("No such entry: {}", id); + exit(1) + }) + .get_coordinates() + .map_err_trace_exit_unwrap(1) // The get action failed + .unwrap_or_else(|| { // if we have Ok(None) + error!("Entry has no coordinates: {}", id); + exit(1) + }); + + let _ = writeln!(stdout, "{}", value).to_exit_code().unwrap_or_exit(); + + let _ = rt + .report_touched(&id) + .map_err_trace_exit_unwrap(1); + }) - let _ = writeln!(rt.stdout(), "{}", value).to_exit_code().unwrap_or_exit(); } diff --git a/bin/core/imag-gps/src/ui.rs b/bin/core/imag-gps/src/ui.rs index 24117109..9bf4a4a1 100644 --- a/bin/core/imag-gps/src/ui.rs +++ b/bin/core/imag-gps/src/ui.rs @@ -17,7 +17,14 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, App, SubCommand}; +use std::path::PathBuf; + +use clap::{Arg, ArgMatches, App, SubCommand}; + +use libimagstore::storeid::IntoStoreId; +use libimagstore::storeid::StoreId; +use libimagrt::runtime::IdPathProvider; +use libimagerror::trace::MapErrTrace; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -42,7 +49,7 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .index(1) .takes_value(true) .required(true) - .multiple(false) + .multiple(true) .help("The entry to add the latitude/longitude to") .value_name("ENTRY")) ) @@ -60,7 +67,7 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .index(1) .takes_value(true) .required(true) - .multiple(false) + .multiple(true) .help("The entry to remove the latitude/longitude from") .value_name("ENTRY")) ) @@ -72,7 +79,7 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .index(1) .takes_value(true) .required(true) - .multiple(false) + .multiple(true) .help("The entry to get the latitude/longitude from") .value_name("ENTRY")) .arg(Arg::with_name("format-json") @@ -89,3 +96,57 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .help("Print as = pairs (2 lines, default)")) ) } + +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(matches: &ArgMatches) -> Vec { + match matches.subcommand() { + ("add", Some(subm)) => { + subm.values_of("entry") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1) + }, + + ("remove", Some(subm)) => { + subm.values_of("entry") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1) + }, + + ("get", Some(subm)) => { + subm.values_of("get-ids") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1) + }, + + (other, _) => { + error!("Not a known command: {}", other); + ::std::process::exit(1) + } + } + } +} diff --git a/bin/core/imag-grep/src/main.rs b/bin/core/imag-grep/src/main.rs index 223849c9..e9db9b7b 100644 --- a/bin/core/imag-grep/src/main.rs +++ b/bin/core/imag-grep/src/main.rs @@ -122,5 +122,9 @@ fn show(rt: &Runtime, e: &Entry, re: &Regex, opts: &Options, count: &mut usize) let _ = writeln!(rt.stdout(), "").to_exit_code().unwrap_or_exit(); *count += 1; } + + let _ = rt + .report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); } diff --git a/bin/core/imag-ids/src/main.rs b/bin/core/imag-ids/src/main.rs index 63998ce2..56c31026 100644 --- a/bin/core/imag-ids/src/main.rs +++ b/bin/core/imag-ids/src/main.rs @@ -53,6 +53,7 @@ use std::process::exit; use filters::filter::Filter; +use libimagstore::storeid::StoreId; use libimagrt::setup::generate_runtime_setup; use libimagerror::trace::MapErrTrace; use libimagerror::iter::TraceIterator; @@ -88,35 +89,50 @@ fn main() { id_filters::header_filter_lang::parse(&query) }); - rt.store() - .entries() - .map_err_trace_exit_unwrap(1) - .trace_unwrap_exit(1) - .filter(|id| collection_filter.filter(id)) - .filter(|id| match query_filter.as_ref() { - None => true, - Some(qf) => { - let entry = rt - .store() - .get(id.clone()) - .map_err_trace_exit_unwrap(1) - .unwrap_or_else(|| { - error!("Tried to get '{}', but it does not exist!", id); - exit(1) - }); + let iterator = if rt.ids_from_stdin() { + debug!("Fetching IDs from stdin..."); + let ids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1); + Box::new(ids.into_iter().map(Ok)) + as Box>> + } else { + Box::new(rt.store().entries().map_err_trace_exit_unwrap(1)) + as Box>> + } + .trace_unwrap_exit(1) + .filter(|id| collection_filter.filter(id)) + .filter(|id| match query_filter.as_ref() { + None => true, + Some(qf) => { + let entry = rt + .store() + .get(id.clone()) + .map_err_trace_exit_unwrap(1) + .unwrap_or_else(|| { + error!("Tried to get '{}', but it does not exist!", id); + exit(1) + }); - qf.filter(&entry) - } - }) - .map(|id| if print_storepath { - id - } else { - id.without_base() - }) - .for_each(|id| { - let _ = writeln!(rt.stdout(), "{}", id.to_str().map_err_trace_exit_unwrap(1)) + qf.filter(&entry) + } + }) + .map(|id| if print_storepath { + id + } else { + id.without_base() + }); + + let mut stdout = rt.stdout(); + trace!("Got output: {:?}", stdout); + + iterator.for_each(|id| { + rt.report_touched(&id).map_err_trace_exit_unwrap(1); + if !rt.output_is_pipe() { + let id = id.to_str().map_err_trace_exit_unwrap(1); + trace!("Writing to {:?}", stdout); + let _ = writeln!(stdout, "{}", id) .to_exit_code() .unwrap_or_exit(); - }) + } + }) } diff --git a/bin/core/imag-ids/src/ui.rs b/bin/core/imag-ids/src/ui.rs index 61007f14..8d36b188 100644 --- a/bin/core/imag-ids/src/ui.rs +++ b/bin/core/imag-ids/src/ui.rs @@ -17,7 +17,10 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, App, SubCommand}; +use clap::{Arg, ArgMatches, App, SubCommand}; + +use libimagstore::storeid::StoreId; +use libimagrt::runtime::IdPathProvider; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -49,3 +52,10 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .after_help(include_str!("../static/language-doc.md")) } +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(_matches: &ArgMatches) -> Vec { + error!("imag-ids does not get IDs via CLI, only via stdin if applying a filter!"); + ::std::process::exit(1) + } +} diff --git a/bin/core/imag-link/src/main.rs b/bin/core/imag-link/src/main.rs index ab4a345e..5d83d6be 100644 --- a/bin/core/imag-link/src/main.rs +++ b/bin/core/imag-link/src/main.rs @@ -157,9 +157,12 @@ fn link_from_to<'a, I>(rt: &'a Runtime, from: &'a str, to: I) ::std::process::exit(1); }); - let _ = from_entry + let iter = from_entry .add_external_link(rt.store(), url) - .map_err_trace_exit_unwrap(1); + .map_err_trace_exit_unwrap(1) + .into_iter(); + + let _ = rt.report_all_touched(iter).map_err_trace_exit_unwrap(1); } else { debug!("Linking internally: {:?} -> {:?}", from, entry); @@ -181,88 +184,91 @@ fn link_from_to<'a, I>(rt: &'a Runtime, from: &'a str, to: I) let _ = from_entry .add_internal_link(&mut to_entry) .map_err_trace_exit_unwrap(1); + + let _ = rt + .report_touched(to_entry.get_location()) + .map_err_trace_exit_unwrap(1); } + info!("Ok: {} -> {}", from, entry); } + + let _ = rt + .report_touched(from_entry.get_location()) + .map_err_trace_exit_unwrap(1); } fn remove_linking(rt: &Runtime) { - - fn get_from_entry<'a>(rt: &'a Runtime) -> Option> { - rt.cli() - .subcommand_matches("remove") - .unwrap() // safe, we know there is an "remove" subcommand - .value_of("from") - .and_then(|from_name| { - match get_entry_by_name(rt, from_name) { - Err(e) => { - debug!("We couldn't get the entry from name: '{:?}'", from_name); - trace_error(&e); None - }, - Ok(Some(e)) => Some(e), - Ok(None) => None, - } - - }) - } - - let mut from = match get_from_entry(&rt) { - None => warn_exit("No 'from' entry", 1), - Some(s) => s, - }; - - rt.cli() + let mut from = rt.cli() .subcommand_matches("remove") - .unwrap() - .values_of("to") - .map(|values| { - for (entry, value) in values.map(|v| (get_entry_by_name(rt, v), v)) { - match entry { - Err(e) => trace_error(&e), - Ok(Some(mut to_entry)) => { - let _ = to_entry - .remove_internal_link(&mut from) - .map_err_trace_exit_unwrap(1); - }, - Ok(None) => { - // looks like this is not an entry, but a filesystem URI and therefor an - // external link...? - if PathBuf::from(value).is_file() { - let url = Url::parse(value).unwrap_or_else(|e| { - error!("Error parsing URL: {:?}", e); - ::std::process::exit(1); - }); - from.remove_external_link(rt.store(), url).map_err_trace_exit_unwrap(1); - info!("Ok: {}", value); - } else { - warn!("Entry not found: {:?}", value); - } - } + .unwrap() // safe, we know there is an "remove" subcommand + .value_of("from") + .map(PathBuf::from) + .map(|id| { + rt.store() + .get(id) + .map_err_trace_exit_unwrap(1) + .ok_or_else(|| warn_exit("No 'from' entry", 1)) + .unwrap() // safe by line above + }) + .unwrap(); + + rt.ids::<::ui::PathProvider>() + .map_err_trace_exit_unwrap(1) + .into_iter() + .for_each(|id| match rt.store().get(id.clone()) { + Err(e) => trace_error(&e), + Ok(Some(mut to_entry)) => { + let _ = to_entry + .remove_internal_link(&mut from) + .map_err_trace_exit_unwrap(1); + + let _ = rt + .report_touched(to_entry.get_location()) + .map_err_trace_exit_unwrap(1); + }, + Ok(None) => { + // looks like this is not an entry, but a filesystem URI and therefor an + // external link...? + if id.local().is_file() { + let pb = id.local().to_str().unwrap_or_else(|| { + warn!("Not StoreId and not a Path: {}", id); + ::std::process::exit(1); + }); + let url = Url::parse(pb).unwrap_or_else(|e| { + error!("Error parsing URL: {:?}", e); + ::std::process::exit(1); + }); + from.remove_external_link(rt.store(), url).map_err_trace_exit_unwrap(1); + info!("Ok: {}", id); + } else { + warn!("Entry not found: {:?}", id); } } }); + + let _ = rt + .report_touched(from.get_location()) + .map_err_trace_exit_unwrap(1); } fn unlink(rt: &Runtime) { - use libimagerror::iter::TraceIterator; - use libimagstore::iter::get::StoreIdGetIteratorExtension; + rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1).into_iter().for_each(|id| { + rt.store() + .get(id.clone()) + .map_err_trace_exit_unwrap(1) + .unwrap_or_else(|| { + warn!("No entry for {}", id); + ::std::process::exit(1) + }) + .unlink(rt.store()) + .map_err_trace_exit_unwrap(1); - let _ = rt - .cli() - .subcommand_matches("unlink") - .unwrap() // checked in main() - .values_of("from") - .unwrap() // checked by clap - .map(PathBuf::from) - .collect::>().into_iter() // for lifetime inference - .map(StoreId::new_baseless) - .into_get_iter(rt.store()) - .trace_unwrap_exit(1) - .filter_map(|x| x) - .map(|mut entry| entry.unlink(rt.store())) - .trace_unwrap_exit(1) - .collect::>(); + let _ = rt + .report_touched(&id) + .map_err_trace_exit_unwrap(1); + }); } fn list_linkings(rt: &Runtime) { @@ -276,8 +282,8 @@ fn list_linkings(rt: &Runtime) { let mut tab = ::prettytable::Table::new(); tab.set_titles(row!["#", "Link"]); - for entry in cmd.values_of("entries").unwrap() { // safed by clap - match rt.store().get(PathBuf::from(entry)) { + rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1).into_iter().for_each(|id| { + match rt.store().get(id.clone()) { Ok(Some(entry)) => { for (i, link) in entry.get_internal_links().map_err_trace_exit_unwrap(1).enumerate() { let link = link @@ -314,11 +320,20 @@ fn list_linkings(rt: &Runtime) { } }) } + + let _ = rt + .report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); + }, - Ok(None) => warn!("Not found: {}", entry), + Ok(None) => warn!("Not found: {}", id), Err(e) => trace_error(&e), } - } + + let _ = rt + .report_touched(&id) + .map_err_trace_exit_unwrap(1); + }); if !list_plain { let out = rt.stdout(); diff --git a/bin/core/imag-link/src/ui.rs b/bin/core/imag-link/src/ui.rs index 3f3136eb..5ca5fca6 100644 --- a/bin/core/imag-link/src/ui.rs +++ b/bin/core/imag-link/src/ui.rs @@ -17,7 +17,15 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, App, SubCommand}; +use std::path::PathBuf; + +use clap::{Arg, ArgMatches, App, SubCommand}; + +use libimagstore::storeid::StoreId; +use libimagstore::storeid::IntoStoreId; +use libimagrt::runtime::IdPathProvider; +use libimagerror::trace::MapErrTrace; + pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -101,3 +109,74 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .requires("from") .value_name("ENTRIES")) } + +/// PathProvider +/// +/// This PathProvider does _not_ return the "from" value of the commandline call if no subcommand +/// is given. +/// +/// It has to be fetched by main() by hand. +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(matches: &ArgMatches) -> Vec { + let ids = match matches.subcommand() { + ("remove", Some(subm)) => { + let to = subm.values_of("to") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1); + Some(to) + }, + + ("unlink", Some(subm)) => { + let ids = subm + .values_of("from") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1); + + Some(ids) + }, + + ("list", Some(subm)) => { + let ids = subm + .values_of("entries") + .ok_or_else(|| { + error!("No StoreId found"); + ::std::process::exit(1) + }) + .unwrap() + .into_iter() + .map(PathBuf::from) + .map(|pb| pb.into_storeid()) + .collect::, _>>() + .map_err_trace_exit_unwrap(1); + + Some(ids) + }, + + _ => None, + }; + + ids.unwrap_or_else(|| { + matches.values_of("to") + .unwrap() + .map(|s| PathBuf::from(s).into_storeid().map_err_trace_exit_unwrap(1)) + .collect() + }) + } +} diff --git a/bin/core/imag-mv/src/main.rs b/bin/core/imag-mv/src/main.rs index 3dfb9798..1e77fce8 100644 --- a/bin/core/imag-mv/src/main.rs +++ b/bin/core/imag-mv/src/main.rs @@ -129,6 +129,9 @@ fn main() { }) .map_err_trace_exit_unwrap(1); + let _ = rt.report_touched(&destname) + .map_err_trace_exit_unwrap(1); + // re-add links to moved entry relink(rt.store(), destname, &mut linked_entries); diff --git a/bin/core/imag-ref/src/main.rs b/bin/core/imag-ref/src/main.rs index d273ec90..c0f22f95 100644 --- a/bin/core/imag-ref/src/main.rs +++ b/bin/core/imag-ref/src/main.rs @@ -88,16 +88,20 @@ fn deref(rt: &Runtime) { .map_err_trace_exit_unwrap(1); match rt.store().get(id.clone()).map_err_trace_exit_unwrap(1) { - Some(entry) => entry - .get_path() - .map_err_trace_exit_unwrap(1) - .to_str() - .ok_or_else(|| { - error!("Could not transform path into string!"); - exit(1) - }) - .map(|s| info!("{}", s)) - .ok(), // safe here because we exited already in the error case + Some(entry) => { + entry + .get_path() + .map_err_trace_exit_unwrap(1) + .to_str() + .ok_or_else(|| { + error!("Could not transform path into string!"); + exit(1) + }) + .map(|s| info!("{}", s)) + .ok(); // safe here because we exited already in the error case + + let _ = rt.report_touched(&id).map_err_trace_exit_unwrap(1); + }, None => { error!("No entry for id '{}' found", id); exit(1) diff --git a/bin/core/imag-store/src/create.rs b/bin/core/imag-store/src/create.rs index 6ee5ecfe..866f3aba 100644 --- a/bin/core/imag-store/src/create.rs +++ b/bin/core/imag-store/src/create.rs @@ -62,6 +62,8 @@ pub fn create(rt: &Runtime) { Entry::default_header()) } .map_err_trace_exit_unwrap(1); + + let _ = rt.report_touched(&path).map_err_trace_exit_unwrap(1); } fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &StoreId) -> Result<()> { diff --git a/bin/core/imag-store/src/get.rs b/bin/core/imag-store/src/get.rs index b9324fef..666bbb92 100644 --- a/bin/core/imag-store/src/get.rs +++ b/bin/core/imag-store/src/get.rs @@ -34,8 +34,11 @@ pub fn get(rt: &Runtime) { let path = StoreId::new(store, path).map_err_trace_exit_unwrap(1); debug!("path = {:?}", path); - let _ = match rt.store().get(path).map_err_trace_exit_unwrap(1) { - Some(entry) => print_entry(rt, scmd, entry), + let _ = match rt.store().get(path.clone()).map_err_trace_exit_unwrap(1) { + Some(entry) => { + print_entry(rt, scmd, entry); + let _ = rt.report_touched(&path).map_err_trace_exit_unwrap(1); + }, None => info!("No entry found"), }; } diff --git a/bin/core/imag-store/src/retrieve.rs b/bin/core/imag-store/src/retrieve.rs index 68ef5491..69201f8e 100644 --- a/bin/core/imag-store/src/retrieve.rs +++ b/bin/core/imag-store/src/retrieve.rs @@ -42,11 +42,13 @@ pub fn retrieve(rt: &Runtime) { debug!("path = {:?}", path); rt.store() - .retrieve(path) + .retrieve(path.clone()) .map(|e| print_entry(rt, scmd, e)) .map_dbg_str("No entry") .map_dbg(|e| format!("{:?}", e)) - .map_err_trace() + .map_err_trace_exit_unwrap(1); + + let _ = rt.report_touched(&path).map_err_trace_exit_unwrap(1); }); } diff --git a/bin/core/imag-store/src/update.rs b/bin/core/imag-store/src/update.rs index 981c8089..9bb0d943 100644 --- a/bin/core/imag-store/src/update.rs +++ b/bin/core/imag-store/src/update.rs @@ -36,16 +36,20 @@ pub fn update(rt: &Runtime) { let _ = rt.store() .retrieve(path) .map(|mut locked_e| { - let e = locked_e.deref_mut(); + { + let e = locked_e.deref_mut(); - scmd.value_of("content") - .map(|new_content| { - *e.get_content_mut() = String::from(new_content); - debug!("New content set"); - }); + scmd.value_of("content") + .map(|new_content| { + *e.get_content_mut() = String::from(new_content); + debug!("New content set"); + }); - *e.get_header_mut() = build_toml_header(scmd, e.get_header().clone()); - debug!("New header set"); + *e.get_header_mut() = build_toml_header(scmd, e.get_header().clone()); + debug!("New header set"); + } + + let _ = rt.report_touched(locked_e.get_location()).map_err_trace_exit_unwrap(1); }); } diff --git a/bin/core/imag-store/src/verify.rs b/bin/core/imag-store/src/verify.rs index 8fa00981..9abefb5e 100644 --- a/bin/core/imag-store/src/verify.rs +++ b/bin/core/imag-store/src/verify.rs @@ -47,6 +47,7 @@ pub fn verify(rt: &Runtime) { }; info!("{: >6} | {: >14} | {:?}", verify, content_len, p.deref()); + let _ = rt.report_touched(fle.get_location()).map_err_trace_exit_unwrap(1); status }); diff --git a/bin/core/imag-tag/src/main.rs b/bin/core/imag-tag/src/main.rs index 25fb6707..2b5349d4 100644 --- a/bin/core/imag-tag/src/main.rs +++ b/bin/core/imag-tag/src/main.rs @@ -56,9 +56,7 @@ extern crate toml_query; #[cfg(test)] extern crate env_logger; -use std::path::PathBuf; use std::io::Write; -use std::io::Read; use libimagrt::runtime::Runtime; use libimagrt::setup::generate_runtime_setup; @@ -84,30 +82,7 @@ fn main() { "Direct interface to the store. Use with great care!", build_ui); - let ids : Vec = rt - .cli() - .values_of("id") - .map(|vals| { - vals.map(PathBuf::from).collect() - }).unwrap_or_else(|| { - if !rt.cli().is_present("ids-from-stdin") { - error!("No ids"); - ::std::process::exit(1) - } - - let stdin = rt.stdin().unwrap_or_else(|| { - error!("Cannot get handle to stdin"); - ::std::process::exit(1) - }); - - let mut buf = String::new(); - let _ = stdin.lock().read_to_string(&mut buf).unwrap_or_else(|_| { - error!("Failed to read from stdin"); - ::std::process::exit(1) - }); - - buf.lines().map(PathBuf::from).collect() - }); + let ids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1); rt.cli() .subcommand_name() @@ -116,14 +91,12 @@ fn main() { list(id, &rt) }, "remove" => for id in ids { - let id = PathBuf::from(id); let add = None; let rem = get_remove_tags(rt.cli()); debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem); alter(&rt, id, add, rem); }, "add" => for id in ids { - let id = PathBuf::from(id); let add = get_add_tags(rt.cli()); let rem = None; debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem); @@ -139,11 +112,8 @@ fn main() { }); } -fn alter(rt: &Runtime, id: PathBuf, add: Option>, rem: Option>) { - let path = StoreId::new(Some(rt.store().path().clone()), id).map_err_trace_exit_unwrap(1); - debug!("path = {:?}", path); - - match rt.store().get(path) { +fn alter(rt: &Runtime, path: StoreId, add: Option>, rem: Option>) { + match rt.store().get(path.clone()) { Ok(Some(mut e)) => { debug!("Entry header now = {:?}", e.get_header()); @@ -184,12 +154,13 @@ fn alter(rt: &Runtime, id: PathBuf, add: Option>, rem: Option> trace_error(&e); }, } + + let _ = rt + .report_touched(&path) + .map_err_trace_exit_unwrap(1); } -fn list(id: PathBuf, rt: &Runtime) { - let path = StoreId::new(Some(rt.store().path().clone()), id).map_err_trace_exit_unwrap(1); - debug!("path = {:?}", path); - +fn list(path: StoreId, rt: &Runtime) { let entry = match rt.store().get(path.clone()).map_err_trace_exit_unwrap(1) { Some(e) => e, None => warn_exit("No entry found.", 1), @@ -233,6 +204,10 @@ fn list(id: PathBuf, rt: &Runtime) { .to_exit_code() .unwrap_or_exit(); } + + let _ = rt + .report_touched(&path) + .map_err_trace_exit_unwrap(1); } /// Get the tags which should be added from the commandline @@ -330,7 +305,7 @@ mod tests { debug!("Add-tags: {:?}", add); debug!("Altering things"); - alter(&rt, id.clone(), add, None); + alter(&rt, StoreId::new_baseless(id.clone()).unwrap(), add, None); debug!("Altered"); let test_entry = rt.store().get(id).unwrap().unwrap(); @@ -365,7 +340,7 @@ mod tests { debug!("Rem-tags: {:?}", rem); debug!("Altering things"); - alter(&rt, id.clone(), add, rem); + alter(&rt, StoreId::new_baseless(id.clone()).unwrap(), add, rem); debug!("Altered"); let test_entry = rt.store().get(id).unwrap().unwrap(); @@ -393,7 +368,7 @@ mod tests { debug!("Rem-tags: {:?}", rem); debug!("Altering things"); - alter(&rt, id.clone(), add, rem); + alter(&rt, StoreId::new_baseless(id.clone()).unwrap(), add, rem); debug!("Altered"); let test_entry = rt.store().get(id).unwrap().unwrap(); @@ -421,7 +396,7 @@ mod tests { debug!("Rem-tags: {:?}", rem); debug!("Altering things"); - alter(&rt, id.clone(), add, rem); + alter(&rt, StoreId::new_baseless(id.clone()).unwrap(), add, rem); debug!("Altered"); let test_entry = rt.store().get(id).unwrap().unwrap(); diff --git a/bin/core/imag-tag/src/ui.rs b/bin/core/imag-tag/src/ui.rs index ec1a565f..3d6a46f8 100644 --- a/bin/core/imag-tag/src/ui.rs +++ b/bin/core/imag-tag/src/ui.rs @@ -17,8 +17,14 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, App, ArgGroup, SubCommand}; +use std::path::PathBuf; +use clap::{Arg, ArgMatches, ArgGroup, App, SubCommand}; + +use libimagstore::storeid::StoreId; +use libimagstore::storeid::IntoStoreId; +use libimagrt::runtime::IdPathProvider; +use libimagerror::trace::MapErrTrace; use libimagentrytag::tag::is_tag; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { @@ -30,14 +36,6 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .value_name("ID") .help("Entry to use")) - .arg(Arg::with_name("ids-from-stdin") - .long("ids-from-stdin") - .short("I") - .takes_value(false) - .required(false) - .multiple(false) - .help("Read store ids to tag from stdin")) - .subcommand(SubCommand::with_name("add") .about("Add tags") .version("0.1") @@ -104,3 +102,14 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { ) } + +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(matches: &ArgMatches) -> Vec { + matches.values_of("id") + .unwrap() + .map(|s| PathBuf::from(s).into_storeid().map_err_trace_exit_unwrap(1)) + .collect() + } +} + diff --git a/bin/core/imag-view/src/main.rs b/bin/core/imag-view/src/main.rs index 43ed4b23..859e5af5 100644 --- a/bin/core/imag-view/src/main.rs +++ b/bin/core/imag-view/src/main.rs @@ -49,8 +49,6 @@ extern crate libimagutil; use std::str::FromStr; use std::collections::BTreeMap; use std::io::Write; -use std::io::Read; -use std::path::PathBuf; use std::process::Command; use std::process::exit; @@ -60,7 +58,6 @@ use failure::Error; use failure::err_msg; use libimagrt::setup::generate_runtime_setup; -use libimagrt::runtime::Runtime; use libimagerror::trace::MapErrTrace; use libimagerror::iter::TraceIterator; use libimagerror::io::ToExitCode; @@ -68,8 +65,6 @@ use libimagerror::exit::ExitUnwrap; use libimagentryview::builtin::stdout::StdoutViewer; use libimagentryview::builtin::md::MarkdownViewer; use libimagentryview::viewer::Viewer; -use libimagstore::storeid::IntoStoreId; -use libimagstore::storeid::StoreIdIterator; use libimagstore::iter::get::StoreIdGetIteratorExtension; use libimagstore::store::FileLockEntry; @@ -83,19 +78,28 @@ fn main() { "View entries (readonly)", build_ui); - let entry_ids = entry_ids(&rt); let view_header = rt.cli().is_present("view-header"); let hide_content = rt.cli().is_present("not-view-content"); + let entries = rt.ids::<::ui::PathProvider>() + .map_err_trace_exit_unwrap(1) + .into_iter() + .map(Ok) + .into_get_iter(rt.store()) + .trace_unwrap_exit(1) + .map(|e| { + e.ok_or_else(|| err_msg("Entry not found")) + .map_err(Error::from) + .map_err_trace_exit_unwrap(1) + }); if rt.cli().is_present("in") { - let files = entry_ids - .into_get_iter(rt.store()) - .trace_unwrap_exit(1) - .map(|e| { - e.ok_or_else(|| err_msg("Entry not found")) - .map_err_trace_exit_unwrap(1) + let files = entries + .map(|entry| { + let tmpfile = create_tempfile_for(&entry, view_header, hide_content); + rt.report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); + tmpfile }) - .map(|entry| create_tempfile_for(&entry, view_header, hide_content)) .collect::>(); let mut command = { @@ -170,14 +174,6 @@ fn main() { drop(files); } else { - let iter = entry_ids - .into_get_iter(rt.store()) - .map(|e| { - e.map_err_trace_exit_unwrap(1) - .ok_or_else(|| err_msg("Entry not found")) - .map_err_trace_exit_unwrap(1) - }); - let out = rt.stdout(); let mut outlock = out.lock(); @@ -198,17 +194,22 @@ fn main() { let viewer = MarkdownViewer::new(&rt); let seperator = basesep.map(|s| build_seperator(s, sep_width)); - for (n, entry) in iter.enumerate() { - if n != 0 { - seperator - .as_ref() - .map(|s| writeln!(outlock, "{}", s).to_exit_code().unwrap_or_exit()); - } + entries + .enumerate() + .for_each(|(n, entry)| { + if n != 0 { + seperator + .as_ref() + .map(|s| writeln!(outlock, "{}", s).to_exit_code().unwrap_or_exit()); + } - viewer - .view_entry(&entry, &mut outlock) - .map_err_trace_exit_unwrap(1); - } + viewer + .view_entry(&entry, &mut outlock) + .map_err_trace_exit_unwrap(1); + + rt.report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); + }); } else { let mut viewer = StdoutViewer::new(view_header, !hide_content); @@ -228,48 +229,22 @@ fn main() { } let seperator = basesep.map(|s| build_seperator(s, sep_width)); - for (n, entry) in iter.enumerate() { - if n != 0 { - seperator - .as_ref() - .map(|s| writeln!(outlock, "{}", s).to_exit_code().unwrap_or_exit()); - } + entries + .enumerate() + .for_each(|(n, entry)| { + if n != 0 { + seperator + .as_ref() + .map(|s| writeln!(outlock, "{}", s).to_exit_code().unwrap_or_exit()); + } - viewer - .view_entry(&entry, &mut outlock) - .map_err_trace_exit_unwrap(1); - } - } - } -} + viewer + .view_entry(&entry, &mut outlock) + .map_err_trace_exit_unwrap(1); -fn entry_ids(rt: &Runtime) -> StoreIdIterator { - match rt.cli().values_of("id") { - Some(p) => { - let pathes : Vec = p.map(String::from).collect(); - let iter = pathes.into_iter().map(PathBuf::from).map(PathBuf::into_storeid); - StoreIdIterator::new(Box::new(iter)) - }, - - None => if rt.cli().is_present("entries-from-stdin") { - let stdin = rt.stdin().unwrap_or_else(|| { - error!("Cannot get handle to stdin"); - ::std::process::exit(1) - }); - - let mut buf = String::new(); - let _ = stdin.lock().read_to_string(&mut buf).unwrap_or_else(|_| { - error!("Failed to read from stdin"); - ::std::process::exit(1) - }); - - let lines : Vec = buf.lines().map(String::from).collect(); - let iter = lines.into_iter().map(PathBuf::from).map(PathBuf::into_storeid); - - StoreIdIterator::new(Box::new(iter)) - } else { - error!("Something weird happened. I was not able to find the path of the entries to edit"); - ::std::process::exit(1) + rt.report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); + }); } } } diff --git a/bin/core/imag-view/src/ui.rs b/bin/core/imag-view/src/ui.rs index c97bd3f2..13169640 100644 --- a/bin/core/imag-view/src/ui.rs +++ b/bin/core/imag-view/src/ui.rs @@ -17,17 +17,17 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, ArgGroup, App}; +use std::path::PathBuf; + +use clap::{Arg, ArgMatches, App}; + +use libimagstore::storeid::StoreId; +use libimagstore::storeid::IntoStoreId; +use libimagrt::runtime::IdPathProvider; +use libimagerror::trace::MapErrTrace; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app - .arg(Arg::with_name("entries-from-stdin") - .long("ids-from-stdin") - .short("I") - .required(false) - .multiple(true) - .help("The entry/entries are piped in via stdin")) - .arg(Arg::with_name("id") .index(1) .takes_value(true) @@ -36,10 +36,6 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .help("View these entries at this store path") .value_name("IDs")) - .group(ArgGroup::with_name("input-method") - .args(&["id", "entries-from-stdin"]) - .required(true)) - .arg(Arg::with_name("autowrap") .long("autowrap") .short("w") @@ -91,3 +87,13 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .help("View content. If no value is given, this fails. Possible viewers are configured via the config file.")) } + +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(matches: &ArgMatches) -> Vec { + matches.values_of("id") + .unwrap() + .map(|s| PathBuf::from(s).into_storeid().map_err_trace_exit_unwrap(1)) + .collect() + } +} diff --git a/bin/core/imag/Cargo.toml b/bin/core/imag/Cargo.toml index d40da6c3..ba37bdf8 100644 --- a/bin/core/imag/Cargo.toml +++ b/bin/core/imag/Cargo.toml @@ -18,8 +18,11 @@ build = "build.rs" [build-dependencies] clap = ">=2.16.1" libimagrt = { version = "0.9.0", path = "../../../lib/core/libimagrt" } +libimagerror = { version = "0.9.0", path = "../../../lib/core/libimagerror" } +libimagstore = { version = "0.9.0", path = "../../../lib/core/libimagstore" } libimagentrytag = { version = "0.9.0", path = "../../../lib/entry/libimagentrytag" } libimagutil = { version = "0.9.0", path = "../../../lib/etc/libimagutil" } +log = "0.4.0" [badges] travis-ci = { repository = "matthiasbeyer/imag" } @@ -34,6 +37,7 @@ toml = "0.4" toml-query = "0.7" libimagerror = { version = "0.9.0", path = "../../../lib/core/libimagerror" } +libimagstore = { version = "0.9.0", path = "../../../lib/core/libimagstore" } [dependencies.clap] version = "^2.29" diff --git a/bin/core/imag/build.rs b/bin/core/imag/build.rs index fe2766b1..24bf628a 100644 --- a/bin/core/imag/build.rs +++ b/bin/core/imag/build.rs @@ -19,7 +19,11 @@ extern crate clap; #[macro_use] +extern crate log; +#[macro_use] extern crate libimagrt; +extern crate libimagerror; +extern crate libimagstore; extern crate libimagentrytag; extern crate libimagutil; @@ -45,6 +49,7 @@ mod toplevelbuildscript { macro_rules! gen_mods_buildui { ($(($path:expr, $modulename:ident)$(,)*)*) => ( $( + #[allow(unused)] mod $modulename { include!($path); } diff --git a/bin/domain/imag-bookmark/src/main.rs b/bin/domain/imag-bookmark/src/main.rs index 1a9c1893..eae6fdfa 100644 --- a/bin/domain/imag-bookmark/src/main.rs +++ b/bin/domain/imag-bookmark/src/main.rs @@ -98,10 +98,18 @@ fn add(rt: &Runtime) { .ok_or_else(|| format_err!("No bookmark collection '{}' found", coll)) .map_err_trace_exit_unwrap(1); + let _ = rt + .report_touched(collection.get_location()) + .map_err_trace_exit_unwrap(1); + for url in scmd.values_of("urls").unwrap() { // unwrap saved by clap - let _ = collection + let new_ids = collection .add_link(rt.store(), BookmarkLink::from(url)) .map_err_trace_exit_unwrap(1); + + let _ = rt + .report_all_touched(new_ids.into_iter()) + .map_err_trace_exit_unwrap(1); } info!("Ready"); @@ -112,7 +120,8 @@ fn collection(rt: &Runtime) { if scmd.is_present("add") { // adding a new collection let name = scmd.value_of("add").unwrap(); - if let Ok(_) = BookmarkCollectionStore::new(rt.store(), &name) { + if let Ok(id) = BookmarkCollectionStore::new(rt.store(), &name) { + let _ = rt.report_touched(id.get_location()).map_err_trace_exit_unwrap(1); info!("Created: {}", name); } else { warn!("Creating collection {} failed", name); @@ -139,6 +148,10 @@ fn list(rt: &Runtime) { .ok_or_else(|| format_err!("No bookmark collection '{}' found", coll)) .map_err_trace_exit_unwrap(1); + let _ = rt + .report_touched(collection.get_location()) + .map_err_trace_exit_unwrap(1); + let links = collection.links(rt.store()).map_err_trace_exit_unwrap(1); debug!("Listing..."); for (i, link) in links.enumerate() { @@ -148,8 +161,6 @@ fn list(rt: &Runtime) { } }; debug!("... ready with listing"); - - info!("Ready"); } fn remove(rt: &Runtime) { @@ -161,10 +172,18 @@ fn remove(rt: &Runtime) { .ok_or_else(|| format_err!("No bookmark collection '{}' found", coll)) .map_err_trace_exit_unwrap(1); + let _ = rt + .report_touched(collection.get_location()) + .map_err_trace_exit_unwrap(1); + for url in scmd.values_of("urls").unwrap() { // enforced by clap - collection + let removed_links = collection .remove_link(rt.store(), BookmarkLink::from(url)) .map_err_trace_exit_unwrap(1); + + let _ = rt + .report_all_touched(removed_links.into_iter()) + .map_err_trace_exit_unwrap(1); } info!("Ready"); diff --git a/bin/domain/imag-contact/src/create.rs b/bin/domain/imag-contact/src/create.rs index edac2b2e..a4ac883e 100644 --- a/bin/domain/imag-contact/src/create.rs +++ b/bin/domain/imag-contact/src/create.rs @@ -200,10 +200,13 @@ pub fn create(rt: &Runtime) { if let Some(location) = location { if !scmd.is_present("dont-track") { - rt.store() + let entry = rt.store() .create_from_path(&location) .map_err_trace_exit_unwrap(1); + let _ = rt.report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); + info!("Created entry in store"); } else { info!("Not creating entry in store"); diff --git a/bin/domain/imag-contact/src/main.rs b/bin/domain/imag-contact/src/main.rs index 80cc92cc..dc862080 100644 --- a/bin/domain/imag-contact/src/main.rs +++ b/bin/domain/imag-contact/src/main.rs @@ -122,6 +122,11 @@ fn list(rt: &Runtime) { .trace_unwrap_exit(1) .map(|fle| fle.ok_or_else(|| Error::from(err_msg("StoreId not found".to_owned())))) .trace_unwrap_exit(1) + .map(|fle| { + let _ = rt.report_touched(fle.get_location()) + .map_err_trace_exit_unwrap(1); + fle + }) .map(|e| e.deser()) .trace_unwrap_exit(1) .enumerate(); @@ -163,21 +168,30 @@ fn import(rt: &Runtime) { } if path.is_file() { - let _ = rt + let entry = rt .store() .retrieve_from_path(&path) .map_err_trace_exit_unwrap(1); + + let _ = rt + .report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); } else if path.is_dir() { for entry in WalkDir::new(path).min_depth(1).into_iter() { let entry = entry .map_err(Error::from) .map_err_trace_exit_unwrap(1); + if entry.file_type().is_file() { let pb = PathBuf::from(entry.path()); - let _ = rt + let fle = rt .store() .retrieve_from_path(&pb) .map_err_trace_exit_unwrap(1); + + let _ = rt + .report_touched(fle.get_location()) + .map_err_trace_exit_unwrap(1); info!("Imported: {}", entry.path().to_str().unwrap_or("")); } else { warn!("Ignoring non-file: {}", entry.path().to_str().unwrap_or("")); @@ -216,6 +230,9 @@ fn show(rt: &Runtime) { .unwrap() // exited above .starts_with(&hash) { + let _ = rt + .report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); Some(deser) } else { None @@ -270,6 +287,10 @@ fn find(rt: &Runtime) { || card.fullname().iter().any(|a| str_contains_any(a, &grepstring)); if take { + let _ = rt + .report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); + // optimization so we don't have to parse again in the next step Some((entry, card)) } else { diff --git a/bin/domain/imag-diary/src/create.rs b/bin/domain/imag-diary/src/create.rs index 6fb730f8..9d2adb1f 100644 --- a/bin/domain/imag-diary/src/create.rs +++ b/bin/domain/imag-diary/src/create.rs @@ -43,6 +43,8 @@ pub fn create(rt: &Runtime) { let mut entry = create_entry(rt.store(), &diaryname, rt); + let _ = rt.report_touched(entry.get_location()).map_err_trace_exit_unwrap(1); + let res = if rt.cli().subcommand_matches("create").unwrap().is_present("no-edit") { debug!("Not editing new diary entry"); Ok(()) diff --git a/bin/domain/imag-diary/src/delete.rs b/bin/domain/imag-diary/src/delete.rs index d8ccd47d..ec3592c3 100644 --- a/bin/domain/imag-diary/src/delete.rs +++ b/bin/domain/imag-diary/src/delete.rs @@ -66,6 +66,10 @@ pub fn delete(rt: &Runtime) { return; } + let _ = rt + .report_touched(&to_del_location) + .map_err_trace_exit_unwrap(1); + let _ = rt .store() .delete(to_del_location) diff --git a/bin/domain/imag-diary/src/list.rs b/bin/domain/imag-diary/src/list.rs index e5a2b3ff..31389ae9 100644 --- a/bin/domain/imag-diary/src/list.rs +++ b/bin/domain/imag-diary/src/list.rs @@ -54,6 +54,12 @@ pub fn list(rt: &Runtime) { ids.into_iter() .map(IntoStoreId::into_storeid) .trace_unwrap_exit(1) - .for_each(|id| writeln!(rt.stdout(), "{}", id).to_exit_code().unwrap_or_exit()); + .for_each(|id| { + let _ = rt + .report_touched(&id) + .map_err_trace_exit_unwrap(1); + + writeln!(rt.stdout(), "{}", id).to_exit_code().unwrap_or_exit() + }); } diff --git a/bin/domain/imag-diary/src/view.rs b/bin/domain/imag-diary/src/view.rs index 763d2456..2bd8e047 100644 --- a/bin/domain/imag-diary/src/view.rs +++ b/bin/domain/imag-diary/src/view.rs @@ -41,6 +41,14 @@ pub fn view(rt: &Runtime) { ::std::process::exit(1) })); + let entries = entries.map(|e| { + let _ = rt + .report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); + + e + }); + let out = rt.stdout(); DV::new(hdr).view_entries(entries, &mut out.lock()) .map_err_trace_exit_unwrap(1); diff --git a/bin/domain/imag-habit/src/main.rs b/bin/domain/imag-habit/src/main.rs index 7aa49664..8c2c951d 100644 --- a/bin/domain/imag-habit/src/main.rs +++ b/bin/domain/imag-habit/src/main.rs @@ -152,7 +152,8 @@ fn create(rt: &Runtime) { debug!("Builder = {:?}", hb); - hb.build(rt.store()).map_err_trace_exit_unwrap(1); + let fle = hb.build(rt.store()).map_err_trace_exit_unwrap(1); + let _ = rt.report_touched(fle.get_location()).map_err_trace_exit_unwrap(1); } fn delete(rt: &Runtime) { @@ -370,6 +371,13 @@ fn today(rt: &Runtime, future: bool) { { let mut v = vec![format!("{}", i)]; let mut list = lister_fn(&e); + + { + let _ = rt + .report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); + } + v.append(&mut list); table.add_row(v.iter().map(|s| Cell::new(s)).collect()); empty = false; @@ -426,6 +434,13 @@ fn list(rt: &Runtime) { .for_each(|(i, e)| { let mut v = vec![format!("{}", i)]; let mut list = lister_fn(&e); + + { + let _ = rt + .report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); + } + v.append(&mut list); table.add_row(v.iter().map(|s| Cell::new(s)).collect()); empty = false; @@ -443,7 +458,6 @@ fn show(rt: &Runtime) { .map(String::from) .unwrap(); // safe by clap - fn instance_lister_fn(i: &FileLockEntry) -> Vec { use libimagutil::date::date_to_string; use libimaghabit::instance::HabitInstance; @@ -499,6 +513,13 @@ fn show(rt: &Runtime) { .for_each(|(i, e)| { let mut v = vec![format!("{}", i)]; let mut instances = instance_lister_fn(&e); + + { + let _ = rt + .report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); + } + v.append(&mut instances); table.add_row(v.iter().map(|s| Cell::new(s)).collect()); empty = false; @@ -555,6 +576,12 @@ fn done(rt: &Runtime) { next_instance_name); } + { + let _ = rt + .report_touched(r.get_location()) + .map_err_trace_exit_unwrap(1); + } + } info!("Done."); } diff --git a/bin/domain/imag-log/src/main.rs b/bin/domain/imag-log/src/main.rs index 56d50189..5b6f01d2 100644 --- a/bin/domain/imag-log/src/main.rs +++ b/bin/domain/imag-log/src/main.rs @@ -57,6 +57,7 @@ use libimagerror::trace::MapErrTrace; use libimagerror::io::ToExitCode; use libimagerror::exit::ExitUnwrap; use libimagerror::iter::TraceIterator; +use libimagerror::exit::ExitCode; use libimagdiary::diary::Diary; use libimaglog::log::Log; use libimagstore::iter::get::StoreIdGetIteratorExtension; @@ -156,7 +157,7 @@ fn show(rt: &Runtime) { .into_iter() .map(|(id, entry)| { debug!("Found entry: {:?}", entry); - writeln!(rt.stdout(), + let _ = writeln!(rt.stdout(), "{dname: >10} - {y: >4}-{m:0>2}-{d:0>2}T{H:0>2}:{M:0>2} - {text}", dname = id.diary_name(), y = id.year(), @@ -165,9 +166,14 @@ fn show(rt: &Runtime) { H = id.hour(), M = id.minute(), text = entry.get_content()) - .to_exit_code() + .to_exit_code()?; + + let _ = rt + .report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); + Ok(()) }) - .collect::, _>>() + .collect::, ExitCode>>() .unwrap_or_exit(); } } diff --git a/bin/domain/imag-mail/src/main.rs b/bin/domain/imag-mail/src/main.rs index c7975480..d43653c5 100644 --- a/bin/domain/imag-mail/src/main.rs +++ b/bin/domain/imag-mail/src/main.rs @@ -89,9 +89,11 @@ fn import_mail(rt: &Runtime) { let scmd = rt.cli().subcommand_matches("import-mail").unwrap(); let path = scmd.value_of("path").unwrap(); // enforced by clap - let _ = Mail::import_from_path(rt.store(), path) - .map_err_trace() - .map_info_str("Ok"); + let mail = Mail::import_from_path(rt.store(), path) + .map_info_str("Ok") + .map_err_trace_exit_unwrap(1); + + let _ = rt.report_touched(mail.fle().get_location()).map_err_trace_exit_unwrap(1); } fn list(rt: &Runtime) { @@ -141,7 +143,9 @@ fn list(rt: &Runtime) { id = id, subj = subject, to = to - ).to_exit_code().unwrap_or_exit() + ).to_exit_code().unwrap_or_exit(); + + let _ = rt.report_touched(m.fle().get_location()).map_err_trace_exit_unwrap(1); } let _ = rt.store() diff --git a/bin/domain/imag-notes/src/main.rs b/bin/domain/imag-notes/src/main.rs index 188fe9a3..c7b11300 100644 --- a/bin/domain/imag-notes/src/main.rs +++ b/bin/domain/imag-notes/src/main.rs @@ -109,6 +109,10 @@ fn create(rt: &Runtime) { .map_warn_err_str("Editing failed") .map_err_trace_exit_unwrap(1); } + + let _ = rt + .report_touched(note.get_location()) + .map_err_trace_exit_unwrap(1); } fn delete(rt: &Runtime) { @@ -129,6 +133,10 @@ fn edit(rt: &Runtime) { .edit_content(rt) .map_warn_err_str("Editing failed") .map_err_trace_exit_unwrap(1); + + let _ = rt + .report_touched(note.get_location()) + .map_err_trace_exit_unwrap(1); }) .unwrap_or_else(|| { error!("Cannot find note with name '{}'", name); @@ -156,9 +164,13 @@ fn list(rt: &Runtime) { .iter() .for_each(|note| { let name = note.get_name().map_err_trace_exit_unwrap(1); - writeln!(rt.stdout(), "{}", name) + let _ = writeln!(rt.stdout(), "{}", name) .to_exit_code() - .unwrap_or_exit() + .unwrap_or_exit(); + + let _ = rt + .report_touched(note.get_location()) + .map_err_trace_exit_unwrap(1); }); } diff --git a/bin/domain/imag-timetrack/src/cont.rs b/bin/domain/imag-timetrack/src/cont.rs index 1e81e7af..b9d34f17 100644 --- a/bin/domain/imag-timetrack/src/cont.rs +++ b/bin/domain/imag-timetrack/src/cont.rs @@ -81,11 +81,16 @@ pub fn cont(rt: &Runtime) -> i32 { acc.and_then(|_| { // create a new tracking with the same tag - tracking + let val = tracking .get_timetrack_tag() .and_then(|tag| rt.store().create_timetracking_now(&tag)) .map(|_| 0) - .map_err_trace() + .map_err_trace(); + + let _ = rt.report_touched(tracking.get_location()) + .map_err_trace_exit_unwrap(1); + + val }) }) }, diff --git a/bin/domain/imag-timetrack/src/day.rs b/bin/domain/imag-timetrack/src/day.rs index 378b89dc..e2c05f8d 100644 --- a/bin/domain/imag-timetrack/src/day.rs +++ b/bin/domain/imag-timetrack/src/day.rs @@ -103,6 +103,9 @@ pub fn day(rt: &Runtime) -> i32 { let end = e.get_end_datetime()?; debug!(" -> end = {:?}", end); + let _ = rt.report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); + Ok((tag, start, end)) }) .trace_unwrap_exit(1) diff --git a/bin/domain/imag-timetrack/src/list.rs b/bin/domain/imag-timetrack/src/list.rs index 13de6412..08545f64 100644 --- a/bin/domain/imag-timetrack/src/list.rs +++ b/bin/domain/imag-timetrack/src/list.rs @@ -162,6 +162,9 @@ pub fn list_impl(rt: &Runtime, .collect(); tab.add_row(Row::new(cells)); + let _ = rt.report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); + Ok(tab) }) }) diff --git a/bin/domain/imag-timetrack/src/month.rs b/bin/domain/imag-timetrack/src/month.rs index 7a65f64f..1ca7d686 100644 --- a/bin/domain/imag-timetrack/src/month.rs +++ b/bin/domain/imag-timetrack/src/month.rs @@ -118,6 +118,9 @@ pub fn month(rt: &Runtime) -> i32 { let end = e.get_end_datetime()?; debug!(" -> end = {:?}", end); + let _ = rt.report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); + Ok((tag, start, end)) }) .trace_unwrap_exit(1) diff --git a/bin/domain/imag-timetrack/src/start.rs b/bin/domain/imag-timetrack/src/start.rs index abf18b93..f7db9e08 100644 --- a/bin/domain/imag-timetrack/src/start.rs +++ b/bin/domain/imag-timetrack/src/start.rs @@ -24,9 +24,9 @@ use failure::Error; use libimagrt::runtime::Runtime; use libimagerror::trace::trace_error; +use libimagerror::trace::MapErrTrace; use libimagtimetrack::tag::TimeTrackingTag; use libimagtimetrack::timetrackingstore::TimeTrackStore; -use libimagerror::trace::MapErrTrace; pub fn start(rt: &Runtime) -> i32 { let (_, cmd) = rt.cli().subcommand(); @@ -49,11 +49,18 @@ pub fn start(rt: &Runtime) -> i32 { .map(String::from) .map(TimeTrackingTag::from) .fold(0, |acc, ttt| { - rt.store() - .create_timetracking_at(&start, &ttt) - .map_err_trace() - .map(|_| acc) - .unwrap_or(1) + match rt.store().create_timetracking_at(&start, &ttt) { + Err(e) => { + trace_error(&e); + 1 + }, + Ok(entry) => { + let _ = rt.report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); + + acc + } + } }) } diff --git a/bin/domain/imag-timetrack/src/stop.rs b/bin/domain/imag-timetrack/src/stop.rs index 539c80a4..f46f666d 100644 --- a/bin/domain/imag-timetrack/src/stop.rs +++ b/bin/domain/imag-timetrack/src/stop.rs @@ -34,7 +34,6 @@ use libimagtimetrack::timetrackingstore::*; use libimagtimetrack::iter::filter::has_end_time; use libimagtimetrack::iter::filter::has_one_of_tags; use libimagutil::warn_result::*; -use libimagutil::debug_result::*; pub fn stop(rt: &Runtime) -> i32 { let (_, cmd) = rt.cli().subcommand(); @@ -92,10 +91,18 @@ pub fn stop(rt: &Runtime) -> i32 { // for each of these timetrackings, end them // for each result, print the backtrace (if any) .fold(0, |acc, mut elem| { - elem.set_end_datetime(stop_time.clone()) - .map_dbg(|e| format!("Setting end time worked: {:?}", e)) - .map(|_| acc) - .map_err_trace_exit_unwrap(1) + match elem.set_end_datetime(stop_time.clone()) { + Err(e) => { + trace_error(&e); + 1 + } + Ok(_) => { + format!("Setting end time worked: {:?}", elem); + let _ = rt.report_touched(elem.get_location()) + .map_err_trace_exit_unwrap(1); + acc + } + } }) } diff --git a/bin/domain/imag-timetrack/src/track.rs b/bin/domain/imag-timetrack/src/track.rs index 0e709615..d8f7d7b0 100644 --- a/bin/domain/imag-timetrack/src/track.rs +++ b/bin/domain/imag-timetrack/src/track.rs @@ -81,12 +81,16 @@ pub fn track(rt: &Runtime) -> i32 { .unwrap() // enforced by clap .map(String::from) .map(TimeTrackingTag::from) - .fold(0, |acc, ttt| { - rt.store() - .create_timetracking(&start, &stop, &ttt) - .map_err_trace() - .map(|_| acc) - .unwrap_or(1) + .fold(0, |acc, ttt| match rt.store().create_timetracking(&start, &stop, &ttt) { + Err(e) => { + trace_error(&e); + 1 + }, + Ok(entry) => { + let _ = rt.report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); + acc + } }) } diff --git a/bin/domain/imag-timetrack/src/week.rs b/bin/domain/imag-timetrack/src/week.rs index c8a74520..97797967 100644 --- a/bin/domain/imag-timetrack/src/week.rs +++ b/bin/domain/imag-timetrack/src/week.rs @@ -116,6 +116,9 @@ pub fn week(rt: &Runtime) -> i32 { let end = e.get_end_datetime()?; debug!(" -> end = {:?}", end); + let _ = rt.report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); + Ok((tag, start, end)) }) .trace_unwrap_exit(1) diff --git a/bin/domain/imag-timetrack/src/year.rs b/bin/domain/imag-timetrack/src/year.rs index 2d5c70f1..24fcfc1e 100644 --- a/bin/domain/imag-timetrack/src/year.rs +++ b/bin/domain/imag-timetrack/src/year.rs @@ -116,6 +116,9 @@ pub fn year(rt: &Runtime) -> i32 { let end = e.get_end_datetime()?; debug!(" -> end = {:?}", end); + let _ = rt.report_touched(e.get_location()) + .map_err_trace_exit_unwrap(1); + Ok((tag, start, end)) }) .trace_unwrap_exit(1) diff --git a/bin/domain/imag-wiki/src/main.rs b/bin/domain/imag-wiki/src/main.rs index 6e586009..c8f55267 100644 --- a/bin/domain/imag-wiki/src/main.rs +++ b/bin/domain/imag-wiki/src/main.rs @@ -170,19 +170,28 @@ fn create(rt: &Runtime, wiki_name: &str) { .map_warn_err_str("Safed entry") .map_err_trace_exit_unwrap(1); + let id = entry.get_location(); + if scmd.is_present("create-printid") { let out = rt.stdout(); let mut lock = out.lock(); - let id = entry.get_location(); writeln!(lock, "{}", id).to_exit_code().unwrap_or_exit() } + + let _ = rt + .report_touched(&id) + .map_err_trace_exit_unwrap(1); } fn create_wiki(rt: &Runtime) { - let scmd = rt.cli().subcommand_matches("create-wiki").unwrap(); // safed by clap - let wiki_name = String::from(scmd.value_of("create-wiki-name").unwrap()); // safe by clap - let _ = rt.store().create_wiki(&wiki_name).map_err_trace_exit_unwrap(1); + let scmd = rt.cli().subcommand_matches("create-wiki").unwrap(); // safed by clap + let wiki_name = String::from(scmd.value_of("create-wiki-name").unwrap()); // safe by clap + let (_, index) = rt.store().create_wiki(&wiki_name).map_err_trace_exit_unwrap(1); + + let _ = rt + .report_touched(index.get_location()) + .map_err_trace_exit_unwrap(1); } fn show(rt: &Runtime, wiki_name: &str) { @@ -239,6 +248,10 @@ fn show(rt: &Runtime, wiki_name: &str) { writeln!(outlock, "{}", entry.get_content()) .to_exit_code() .unwrap_or_exit(); + + let _ = rt + .report_touched(entry.get_location()) + .map_err_trace_exit_unwrap(1); } } diff --git a/doc/src/03010-conventions.md b/doc/src/03010-conventions.md index a6cf4d3e..661745d1 100644 --- a/doc/src/03010-conventions.md +++ b/doc/src/03010-conventions.md @@ -104,3 +104,14 @@ Commandline interfaces should also provide a flag "-I" (that's a big i) which marks that the store IDs shall be read from stdin and are not passed via the commandline. + +### IO + +There are minor restrictions how imag tools should do IO. A good rule of thumb +is (but most certainly only applicable when programming an imag tool in Rust): +use `libimagrt` to do IO of any kind. + +For more information, or if not using Rust as programming language: the +documentation of `libimagrt` describes how IO should happen (which output +stream to use, how input should be done). + diff --git a/doc/src/05100-lib-rt.md b/doc/src/05100-lib-rt.md index ca22256c..eddba923 100644 --- a/doc/src/05100-lib-rt.md +++ b/doc/src/05100-lib-rt.md @@ -9,6 +9,82 @@ It also contains the store object and creates it from configuration. the `libimagrt::runtime::Runtime` object is the first complex object that comes to live in a imag binary. + +### IO with libimagrt + +libimagrt also provides IO primitives which should be used by all imag tools and +libraries: + +The IO story in imag is pretty easy. As imag is mainly a CLI tool, IO is either +`stdout` or `stderr` and `stdin`. + + +#### Output + +libimagrt provides getters for an output stream for "normal" output, like +logging, status information, etc. It also provides an output for "touched +entries". + +Whenever an imag tool touches an entry in any way (either reading or writing), +it should report this to libimagrt. libimagrt then does "the right thing" which +is printing it to stdout or swallowing the output. +Normal output (logging, status information, explicitely queried information) +goes to the right sink automatically, that is: + +* If the user provides the appropriate flags, normal output goes to `stderr` and + "touched entries" go to `stdout`. This allows a user to 'chain' imag calls. +* If the user does not provide these flags, normal output goes to `stdout` (for + piping to other tools, e.g. `grep`) and "touched entries" are not printed. + +* `stdin` can be used for reading store-ids which shall be processed by an imag + tool. For example `imag-tag` can receive a list of entries to add tags to via + `stdin` like this: `echo some/entry some/other | imag tag -I add sometag`. + +With these two settings in place, calls to imag can be chained and mixed with +external tools pretty easily: + +``` +imag -O ids where 'some.header == 1' | \ +imag -I -O tag add foo | \ +imag -I -O category set bar | \ +fzf | \ +imag -I tag add baz +``` + +The first line gets all imag entries where `some.header` equals `1`. The touched +entries are printed to `stdout` (`-O`). +The second line tags all entries which are passed via `stdin` (`-I`) with `foo` +and prints them to `stdout` (`-O`) +The third line sets the category for all entries which are passed via `stdin` +with `bar` and prints them to `stdout`. +The fourth line calls the `fzf` program and lets the user select one entry +and the last line reads that entry via `stdin` and tags it with `baz`. + +Automatically detecting the appropriate input/output settings is possible, but +hidden behind a environment-flag, as it is considered experimental. +To test this, set `IMAG_IO_EXPERIMENTAL=1` in your environment. +Note that `stdin` may be detected as "store id stream" when it is actually not. +`libimagrt` can take care of this when passing `--interactive`. + + +#### Input + +`libimagrt` also provides primitives for input. As documented in the paragraph +on "Output", imag tools may get store ids passed via `stdin`. +Hence, imag tools may/can not interactive when passing store ids via `stdin`. +`libimagrt` provides functionality to query data from the user. These functions +automatically fail if the user passes store-ids via `stdin`. + +The next paragraph documents the details of this and may be skipped. + +The user tells imag that `stdin` contains store-ids by setting the `-I` +(`--ids-in`) flag on the commandline. If that flag is given, the interactive +functionality of libimagrt automatically returns an `Err(_)` which can be used +to tell the user what happened and exit the program accordingly. +The user may also provide `--interactive` to tell imag via libimagrt that +`stdin` is indeed not a stream of store-ids even if a pipe is detected. + + ### Long-term TODO - [ ] Merge with `libimagstore` diff --git a/lib/core/libimagrt/src/runtime.rs b/lib/core/libimagrt/src/runtime.rs index 83876ec9..ab0b398c 100644 --- a/lib/core/libimagrt/src/runtime.rs +++ b/lib/core/libimagrt/src/runtime.rs @@ -23,6 +23,8 @@ use std::env; use std::process::exit; use std::io::Stdin; use std::sync::Arc; +use std::io::StdoutLock; +use std::borrow::Borrow; pub use clap::App; use clap::AppSettings; @@ -42,9 +44,11 @@ use io::OutputProxy; use libimagerror::errors::ErrorMsg as EM; use libimagerror::trace::*; use libimagstore::store::Store; +use libimagstore::storeid::StoreId; use libimagstore::file_abstraction::InMemoryFileAbstraction; use libimagutil::debug_result::DebugResult; use spec::CliSpec; +use atty; /// The Runtime object /// @@ -55,6 +59,9 @@ pub struct Runtime<'a> { configuration: Option, cli_matches: ArgMatches<'a>, store: Store, + + has_output_pipe: bool, + has_input_pipe: bool, } impl<'a> Runtime<'a> { @@ -137,11 +144,20 @@ impl<'a> Runtime<'a> { Store::new(storepath, &config) }; + let has_output_pipe = !atty::is(atty::Stream::Stdout); + let has_input_pipe = !atty::is(atty::Stream::Stdin); + + debug!("has output pipe = {}", has_output_pipe); + debug!("has input pipe = {}", has_input_pipe); + store_result.map(|store| Runtime { cli_matches: matches, configuration: config, rtp: rtp, store: store, + + has_output_pipe, + has_input_pipe, }) .context(err_msg("Cannot instantiate runtime")) .map_err(Error::from) @@ -373,6 +389,33 @@ impl<'a> Runtime<'a> { &self.cli_matches } + pub fn ids_from_stdin(&self) -> bool { + self.has_input_pipe + } + + pub fn ids(&self) -> Result> { + use std::io::Read; + + if self.has_input_pipe { + trace!("Getting IDs from stdin..."); + let stdin = ::std::io::stdin(); + let mut lock = stdin.lock(); + + let mut buf = String::new(); + lock.read_to_string(&mut buf) + .map_err(Error::from) + .and_then(|_| { + trace!("Got IDs = {}", buf); + buf.lines() + .map(PathBuf::from) + .map(|id| StoreId::new_baseless(id).map_err(Error::from)) + .collect() + }) + } else { + Ok(T::get_ids(self.cli())) + } + } + /// Get the configuration object pub fn config(&self) -> Option<&Value> { self.configuration.as_ref() @@ -414,8 +457,16 @@ impl<'a> Runtime<'a> { }) } + pub fn output_is_pipe(&self) -> bool { + self.has_output_pipe + } + pub fn stdout(&self) -> OutputProxy { - OutputProxy::Out(::std::io::stdout()) + if self.output_is_pipe() { + OutputProxy::Err(::std::io::stderr()) + } else { + OutputProxy::Out(::std::io::stdout()) + } } pub fn stderr(&self) -> OutputProxy { @@ -423,7 +474,11 @@ impl<'a> Runtime<'a> { } pub fn stdin(&self) -> Option { - Some(::std::io::stdin()) + if self.has_input_pipe { + None + } else { + Some(::std::io::stdin()) + } } /// Helper for handling subcommands which are not available. @@ -504,6 +559,71 @@ impl<'a> Runtime<'a> { .context(EM::IO) .map_err(Error::from) } + + pub fn report_touched(&self, id: &StoreId) -> Result<()> { + let out = ::std::io::stdout(); + let mut lock = out.lock(); + + self.report_touched_id(id, &mut lock) + } + + pub fn report_all_touched(&self, ids: I) -> Result<()> + where ID: Borrow + Sized, + I: Iterator + { + let out = ::std::io::stdout(); + let mut lock = out.lock(); + + for id in ids { + self.report_touched_id(id.borrow(), &mut lock)?; + } + + Ok(()) + } + + #[inline] + fn report_touched_id(&self, id: &StoreId, output: &mut StdoutLock) -> Result<()> { + use std::io::Write; + + if self.output_is_pipe() { + trace!("Reporting: {} to {:?}", id, output); + writeln!(output, "{}", id)?; + } + + Ok(()) + } +} + +/// A trait for the path provider functionality +/// +/// This trait can be implement on a type so that it can provide IDs when given a ArgMatches +/// object. +/// +/// It can be used with Runtime::ids() and libimagrt handles "stdin-provides-ids" cases +/// automatically: +/// +/// ```ignore +/// runtime.ids::()?.iter().for_each(|id| /* ... */) +/// ``` +/// +/// libimagrt does not call the PathProvider if the ids are provided by piping to stdin. +/// +/// +/// # Passed arguments +/// +/// The arguments which are passed into the IdPathProvider::get_ids() function are the _top level +/// ArgMatches_. Traversing might be required in the implementation of the ::get_ids() function. +/// +/// +/// # Returns +/// +/// In case of error, the IdPathProvider::get_ids() function should exit the application +/// with the appropriate error message(s). +/// +/// On success, the StoreId objects to operate on are returned from the ArgMatches. +/// +pub trait IdPathProvider { + fn get_ids(matches: &ArgMatches) -> Vec; } /// Exported for the `imag` command, you probably do not want to use that. diff --git a/lib/domain/libimagbookmark/src/collection.rs b/lib/domain/libimagbookmark/src/collection.rs index 22333690..d5945640 100644 --- a/lib/domain/libimagbookmark/src/collection.rs +++ b/lib/domain/libimagbookmark/src/collection.rs @@ -34,6 +34,7 @@ use libimagstore::store::Store; use libimagstore::store::Entry; use libimagstore::store::FileLockEntry; use libimagstore::storeid::IntoStoreId; +use libimagstore::storeid::StoreId; use libimagentrylink::external::ExternalLinker; use libimagentrylink::external::iter::UrlIter; use libimagentrylink::internal::InternalLinker; @@ -76,10 +77,10 @@ impl<'a> BookmarkCollectionStore<'a> for Store { pub trait BookmarkCollection : Sized + InternalLinker + ExternalLinker { fn links<'a>(&self, store: &'a Store) -> Result>; - fn link_entries(&self) -> Result>; - fn add_link(&mut self, store: &Store, l: Link) -> Result<()>; + fn link_entries(&self) -> Result>; + fn add_link(&mut self, store: &Store, l: Link) -> Result>; fn get_links_matching<'a>(&self, store: &'a Store, r: Regex) -> Result>; - fn remove_link(&mut self, store: &Store, l: Link) -> Result<()>; + fn remove_link(&mut self, store: &Store, l: Link) -> Result>; } impl BookmarkCollection for Entry { @@ -93,7 +94,7 @@ impl BookmarkCollection for Entry { self.get_internal_links().map(|v| v.filter(|id| is_external_link_storeid(id)).collect()) } - fn add_link(&mut self, store: &Store, l: Link) -> Result<()> { + fn add_link(&mut self, store: &Store, l: Link) -> Result> { use link::IntoUrl; l.into_url().and_then(|url| self.add_external_link(store, url)) } @@ -103,7 +104,7 @@ impl BookmarkCollection for Entry { self.get_external_links(store).map(|iter| iter.matching_regex(r)) } - fn remove_link(&mut self, store: &Store, l: Link) -> Result<()> { + fn remove_link(&mut self, store: &Store, l: Link) -> Result> { use link::IntoUrl; l.into_url().and_then(|url| self.remove_external_link(store, url)) } diff --git a/lib/domain/libimagwiki/src/store.rs b/lib/domain/libimagwiki/src/store.rs index 89b7093b..5ee5bd32 100644 --- a/lib/domain/libimagwiki/src/store.rs +++ b/lib/domain/libimagwiki/src/store.rs @@ -17,6 +17,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use libimagstore::store::FileLockEntry; use libimagstore::store::Store; use libimagstore::storeid::StoreId; use libimagstore::storeid::IntoStoreId; @@ -30,10 +31,10 @@ pub trait WikiStore { fn get_wiki<'a, 'b>(&'a self, name: &'b str) -> Result>>; fn create_wiki<'a, 'b>(&'a self, name: &'b str) - -> Result>; + -> Result<(Wiki<'a, 'b>, FileLockEntry<'a>)>; fn retrieve_wiki<'a, 'b>(&'a self, name: &'b str) - -> Result>; + -> Result<(Wiki<'a, 'b>, FileLockEntry<'a>)>; } @@ -59,20 +60,23 @@ impl WikiStore for Store { /// Ob success, an empty Wiki entry with the name `index` is created inside the wiki. Later, new /// entries are automatically linked to this entry. /// - fn create_wiki<'a, 'b>(&'a self, name: &'b str) -> Result> { + fn create_wiki<'a, 'b>(&'a self, name: &'b str) -> Result<(Wiki<'a, 'b>, FileLockEntry<'a>)> { debug!("Trying to get wiki '{}'", name); let wiki = Wiki::new(self, name); - let _ = wiki.create_index_page()?; - Ok(wiki) + let index = wiki.create_index_page()?; + Ok((wiki, index)) } fn retrieve_wiki<'a, 'b>(&'a self, name: &'b str) - -> Result> + -> Result<(Wiki<'a, 'b>, FileLockEntry<'a>)> { match self.get_wiki(name)? { None => self.create_wiki(name), - Some(wiki) => Ok(wiki), + Some(wiki) => { + let index = wiki.get_index_page()?; + Ok((wiki, index)) + }, } } diff --git a/lib/domain/libimagwiki/src/wiki.rs b/lib/domain/libimagwiki/src/wiki.rs index c3888710..5e87c150 100644 --- a/lib/domain/libimagwiki/src/wiki.rs +++ b/lib/domain/libimagwiki/src/wiki.rs @@ -30,6 +30,7 @@ use libimagstore::storeid::StoreIdIteratorWithStore; use libimagentrylink::internal::InternalLinker; use failure::Fallible as Result; +use failure::Error; use failure::err_msg; pub struct Wiki<'a, 'b>(&'a Store, &'b str); @@ -56,6 +57,16 @@ impl<'a, 'b> Wiki<'a, 'b> { self.0.create(sid) } + pub(crate) fn get_index_page(&self) -> Result> { + let path = PathBuf::from(format!("{}/index", self.1)); + let sid = ::module_path::ModuleEntryPath::new(path).into_storeid()?; + + self.0 + .get(sid) + .map_err(Error::from)? + .ok_or_else(|| Error::from(err_msg("Missing index"))) + } + pub fn get_entry>(&self, entry_name: EN) -> Result>> { let path = PathBuf::from(format!("{}/{}", self.1, entry_name.as_ref())); let sid = ::module_path::ModuleEntryPath::new(path).into_storeid()?; diff --git a/lib/entry/libimagentrylink/src/external.rs b/lib/entry/libimagentrylink/src/external.rs index 6b951d46..d4c6956b 100644 --- a/lib/entry/libimagentrylink/src/external.rs +++ b/lib/entry/libimagentrylink/src/external.rs @@ -108,13 +108,13 @@ pub trait ExternalLinker : InternalLinker { fn get_external_links<'a>(&self, store: &'a Store) -> Result>; /// Set the external links for the implementor object - fn set_external_links(&mut self, store: &Store, links: Vec) -> Result<()>; + fn set_external_links(&mut self, store: &Store, links: Vec) -> Result>; /// Add an external link to the implementor object - fn add_external_link(&mut self, store: &Store, link: Url) -> Result<()>; + fn add_external_link(&mut self, store: &Store, link: Url) -> Result>; /// Remove an external link from the implementor object - fn remove_external_link(&mut self, store: &Store, link: Url) -> Result<()>; + fn remove_external_link(&mut self, store: &Store, link: Url) -> Result>; } @@ -322,13 +322,20 @@ impl ExternalLinker for Entry { } /// Set the external links for the implementor object - fn set_external_links(&mut self, store: &Store, links: Vec) -> Result<()> { + /// + /// # Return Value + /// + /// Returns the StoreIds which were newly created for the new external links, if there are more + /// external links than before. + /// If there are less external links than before, an empty vec![] is returned. + /// + fn set_external_links(&mut self, store: &Store, links: Vec) -> Result> { // Take all the links, generate a SHA sum out of each one, filter out the already existing // store entries and store the other URIs in the header of one FileLockEntry each, in // the path /link/external/ debug!("Iterating {} links = {:?}", links.len(), links); - for link in links { // for all links + links.into_iter().map(|link| { let hash = hex::encode(Sha1::digest(&link.as_str().as_bytes())); let file_id = ModuleEntryPath::new(format!("external/{}", hash)).into_storeid() @@ -341,6 +348,8 @@ impl ExternalLinker for Entry { debug!("Hash = '{:?}'", hash); debug!("StoreId = '{:?}'", file_id); + let link_already_exists = store.get(file_id.clone())?.is_some(); + // retrieve the file from the store, which implicitely creates the entry if it does not // exist let mut file = store @@ -375,13 +384,27 @@ impl ExternalLinker for Entry { // then add an internal link to the new file or return an error if this fails let _ = self.add_internal_link(file.deref_mut())?; debug!("Error adding internal link"); - } - debug!("Ready iterating"); - Ok(()) + + Ok((link_already_exists, file_id)) + }) + .filter_map(|res| match res { + Ok((exists, entry)) => if exists { Some(Ok(entry)) } else { None }, + Err(e) => Some(Err(e)) + }) + .collect() } /// Add an external link to the implementor object - fn add_external_link(&mut self, store: &Store, link: Url) -> Result<()> { + /// + /// # Return Value + /// + /// (See ExternalLinker::set_external_links()) + /// + /// Returns the StoreIds which were newly created for the new external links, if there are more + /// external links than before. + /// If there are less external links than before, an empty vec![] is returned. + /// + fn add_external_link(&mut self, store: &Store, link: Url) -> Result> { // get external links, add this one, save them debug!("Getting links"); self.get_external_links(store) @@ -396,7 +419,16 @@ impl ExternalLinker for Entry { } /// Remove an external link from the implementor object - fn remove_external_link(&mut self, store: &Store, link: Url) -> Result<()> { + /// + /// # Return Value + /// + /// (See ExternalLinker::set_external_links()) + /// + /// Returns the StoreIds which were newly created for the new external links, if there are more + /// external links than before. + /// If there are less external links than before, an empty vec![] is returned. + /// + fn remove_external_link(&mut self, store: &Store, link: Url) -> Result> { // get external links, remove this one, save them self.get_external_links(store) .and_then(|links| {