Merge branch 'runtime-io-story'

This merge introduces the new runtime IO system, finally!

Now, piping imag commands into eachother as well as using standard unix
tools for piping is possible! Awesome!

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
Matthias Beyer 2018-11-06 21:26:56 +01:00
commit ecf4cead93
54 changed files with 1229 additions and 572 deletions

View file

@ -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::<Vec<_>>();
}
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::<Vec<_>>();
}
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::<Vec<_>>();
});
} 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::<Vec<_>>();
}
}

View file

@ -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<StoreId> {
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::<Result<Vec<_>, _>>()
.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::<Result<Vec<_>, _>>()
.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::<Result<Vec<_>, _>>()
.map_err_trace_exit_unwrap(1)
},
(other, _) => {
error!("Not a known command: {}", other);
::std::process::exit(1)
}
}
}
}

View file

@ -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();

View file

@ -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<StoreId> {
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::<Result<Vec<_>, _>>()
.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::<Result<Vec<_>, _>>()
.map_err_trace_exit_unwrap(1)
},
(other, _) => {
error!("Not a known command: {}", other);
::std::process::exit(1)
}
}
}
}

View file

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

View file

@ -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<StoreId> {
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::<Result<Vec<_>, _>>()
.map_err_trace_exit_unwrap(1)
}
}

View file

@ -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();
}

View file

@ -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 <key>=<value> pairs (2 lines, default)"))
)
}
pub struct PathProvider;
impl IdPathProvider for PathProvider {
fn get_ids(matches: &ArgMatches) -> Vec<StoreId> {
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::<Result<Vec<_>, _>>()
.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::<Result<Vec<_>, _>>()
.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::<Result<Vec<_>, _>>()
.map_err_trace_exit_unwrap(1)
},
(other, _) => {
error!("Not a known command: {}", other);
::std::process::exit(1)
}
}
}
}

View file

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

View file

@ -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<Iterator<Item = Result<StoreId, _>>>
} else {
Box::new(rt.store().entries().map_err_trace_exit_unwrap(1))
as Box<Iterator<Item = Result<StoreId, _>>>
}
.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();
})
}
})
}

View file

@ -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<StoreId> {
error!("imag-ids does not get IDs via CLI, only via stdin if applying a filter!");
::std::process::exit(1)
}
}

View file

@ -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<FileLockEntry<'a>> {
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::<Vec<PathBuf>>().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::<Vec<_>>();
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();

View file

@ -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<StoreId> {
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::<Result<Vec<_>, _>>()
.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::<Result<Vec<_>, _>>()
.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::<Result<Vec<_>, _>>()
.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()
})
}
}

View file

@ -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);

View file

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

View file

@ -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<()> {

View file

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

View file

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

View file

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

View file

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

View file

@ -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<PathBuf> = 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<Vec<Tag>>, rem: Option<Vec<Tag>>) {
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<Vec<Tag>>, rem: Option<Vec<Tag>>) {
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<Vec<Tag>>, rem: Option<Vec<Tag>>
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();

View file

@ -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<StoreId> {
matches.values_of("id")
.unwrap()
.map(|s| PathBuf::from(s).into_storeid().map_err_trace_exit_unwrap(1))
.collect()
}
}

View file

@ -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::<Vec<_>>();
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<String> = 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<String> = 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);
});
}
}
}

View file

@ -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<StoreId> {
matches.values_of("id")
.unwrap()
.map(|s| PathBuf::from(s).into_storeid().map_err_trace_exit_unwrap(1))
.collect()
}
}

View file

@ -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"

View file

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

View file

@ -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");

View file

@ -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");

View file

@ -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("<non UTF-8 path>"));
} else {
warn!("Ignoring non-file: {}", entry.path().to_str().unwrap_or("<non UTF-8 path>"));
@ -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 {

View file

@ -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(())

View file

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

View file

@ -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()
});
}

View file

@ -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);

View file

@ -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<String> {
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.");
}

View file

@ -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::<Result<Vec<()>, _>>()
.collect::<Result<Vec<()>, ExitCode>>()
.unwrap_or_exit();
}
}

View file

@ -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()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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).

View file

@ -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`

View file

@ -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<Value>,
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<T: IdPathProvider>(&self) -> Result<Vec<StoreId>> {
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<Stdin> {
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<ID, I>(&self, ids: I) -> Result<()>
where ID: Borrow<StoreId> + Sized,
I: Iterator<Item = ID>
{
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::<PathProvider>()?.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<StoreId>;
}
/// Exported for the `imag` command, you probably do not want to use that.

View file

@ -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<UrlIter<'a>>;
fn link_entries(&self) -> Result<Vec<StoreLink>>;
fn add_link(&mut self, store: &Store, l: Link) -> Result<()>;
fn link_entries(&self) -> Result<Vec<StoreLink>>;
fn add_link(&mut self, store: &Store, l: Link) -> Result<Vec<StoreId>>;
fn get_links_matching<'a>(&self, store: &'a Store, r: Regex) -> Result<LinksMatchingRegexIter<'a>>;
fn remove_link(&mut self, store: &Store, l: Link) -> Result<()>;
fn remove_link(&mut self, store: &Store, l: Link) -> Result<Vec<StoreId>>;
}
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<Vec<StoreId>> {
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<Vec<StoreId>> {
use link::IntoUrl;
l.into_url().and_then(|url| self.remove_external_link(store, url))
}

View file

@ -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<Option<Wiki<'a, 'b>>>;
fn create_wiki<'a, 'b>(&'a self, name: &'b str)
-> Result<Wiki<'a, 'b>>;
-> Result<(Wiki<'a, 'b>, FileLockEntry<'a>)>;
fn retrieve_wiki<'a, 'b>(&'a self, name: &'b str)
-> Result<Wiki<'a, 'b>>;
-> 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<Wiki<'a, 'b>> {
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<Wiki<'a, 'b>>
-> 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))
},
}
}

View file

@ -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<FileLockEntry<'a>> {
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<EN: AsRef<str>>(&self, entry_name: EN) -> Result<Option<FileLockEntry<'a>>> {
let path = PathBuf::from(format!("{}/{}", self.1, entry_name.as_ref()));
let sid = ::module_path::ModuleEntryPath::new(path).into_storeid()?;

View file

@ -108,13 +108,13 @@ pub trait ExternalLinker : InternalLinker {
fn get_external_links<'a>(&self, store: &'a Store) -> Result<UrlIter<'a>>;
/// Set the external links for the implementor object
fn set_external_links(&mut self, store: &Store, links: Vec<Url>) -> Result<()>;
fn set_external_links(&mut self, store: &Store, links: Vec<Url>) -> Result<Vec<StoreId>>;
/// 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<Vec<StoreId>>;
/// 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<Vec<StoreId>>;
}
@ -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<Url>) -> 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<Url>) -> Result<Vec<StoreId>> {
// 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/<SHA of the URL>
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<Vec<StoreId>> {
// 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<Vec<StoreId>> {
// get external links, remove this one, save them
self.get_external_links(store)
.and_then(|links| {