Remove calls to exit() and replace them with error propagation up to main()

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
Matthias Beyer 2019-10-19 08:54:57 +02:00
parent 2811ad3d4d
commit 0e14f953ca

View file

@ -37,7 +37,7 @@
#[macro_use] extern crate log; #[macro_use] extern crate log;
extern crate clap; extern crate clap;
extern crate url; extern crate url;
extern crate failure; #[macro_use] extern crate failure;
#[macro_use] extern crate prettytable; #[macro_use] extern crate prettytable;
#[cfg(test)] extern crate toml; #[cfg(test)] extern crate toml;
#[cfg(test)] extern crate toml_query; #[cfg(test)] extern crate toml_query;
@ -65,18 +65,14 @@ use failure::err_msg;
use libimagentryurl::linker::UrlLinker; use libimagentryurl::linker::UrlLinker;
use libimagentrylink::linkable::Linkable; use libimagentrylink::linkable::Linkable;
use libimagentrylink::storecheck::StoreLinkConsistentExt; use libimagentrylink::storecheck::StoreLinkConsistentExt;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagrt::runtime::Runtime; use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication; use libimagrt::application::ImagApplication;
use libimagstore::store::FileLockEntry; use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId; use libimagstore::storeid::StoreId;
use libimagutil::warn_exit::warn_exit;
use libimagutil::warn_result::*;
use url::Url; use url::Url;
use failure::Fallible as Result; use failure::Fallible as Result;
use failure::Error;
use clap::App; use clap::App;
mod ui; mod ui;
@ -89,46 +85,31 @@ pub enum ImagLink {}
impl ImagApplication for ImagLink { impl ImagApplication for ImagLink {
fn run(rt: Runtime) -> Result<()> { fn run(rt: Runtime) -> Result<()> {
if rt.cli().is_present("check-consistency") { if rt.cli().is_present("check-consistency") {
let exit_code = match rt.store().check_link_consistency() { rt.store().check_link_consistency()?;
Ok(_) => {
info!("Store is consistent"); info!("Store is consistent");
0
}
Err(e) => {
trace_error(&e);
1
}
};
::std::process::exit(exit_code);
} }
let _ = rt.cli() if let Some(name) = rt.cli().subcommand_name() {
.subcommand_name()
.map(|name| {
match name { match name {
"remove" => remove_linking(&rt), "remove" => remove_linking(&rt),
"unlink" => unlink(&rt), "unlink" => unlink(&rt),
"list" => list_linkings(&rt), "list" => list_linkings(&rt),
other => { other => {
debug!("Unknown command"); debug!("Unknown command");
let _ = rt.handle_unknown_subcommand("imag-link", other, rt.cli()) if rt.handle_unknown_subcommand("imag-link", other, rt.cli())?.success() {
.map_err_trace_exit_unwrap() Ok(())
.code() } else {
.map(::std::process::exit); Err(format_err!("Subcommand failed"))
}
}, },
} }
})
.or_else(|| {
if let (Some(from), Some(to)) = (rt.cli().value_of("from"), rt.cli().values_of("to")) {
Some(link_from_to(&rt, from, to))
} else { } else {
warn_exit("No commandline call", 1) if let (Some(from), Some(to)) = (rt.cli().value_of("from"), rt.cli().values_of("to")) {
link_from_to(&rt, from, to)
} else {
Err(err_msg("No commandline call"))
}
} }
})
.ok_or_else(|| err_msg("No commandline call".to_owned()))
.map_err_trace_exit_unwrap();
Ok(())
} }
fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> { fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
@ -156,144 +137,103 @@ fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockE
result result
} }
fn link_from_to<'a, I>(rt: &'a Runtime, from: &'a str, to: I) fn link_from_to<'a, I>(rt: &'a Runtime, from: &'a str, to: I) -> Result<()>
where I: Iterator<Item = &'a str> where I: Iterator<Item = &'a str>
{ {
let mut from_entry = match get_entry_by_name(rt, from).map_err_trace_exit_unwrap() { let mut from_entry = get_entry_by_name(rt, from)?.ok_or_else(|| err_msg("No 'from' entry"))?;
Some(e) => e,
None => {
debug!("No 'from' entry");
warn_exit("No 'from' entry", 1)
},
};
for entry in to { for entry in to {
debug!("Handling 'to' entry: {:?}", entry); debug!("Handling 'to' entry: {:?}", entry);
if rt.store().get(PathBuf::from(entry)).map_err_trace_exit_unwrap().is_none() { if rt.store().get(PathBuf::from(entry))?.is_none() {
debug!("Linking externally: {:?} -> {:?}", from, entry); debug!("Linking externally: {:?} -> {:?}", from, entry);
let url = Url::parse(entry).unwrap_or_else(|e| { let url = Url::parse(entry).map_err(|e| format_err!("Error parsing URL: {:?}", e))?;
error!("Error parsing URL: {:?}", e);
::std::process::exit(1);
});
let iter = from_entry let iter = from_entry
.add_url(rt.store(), url) .add_url(rt.store(), url)?
.map_err_trace_exit_unwrap()
.into_iter(); .into_iter();
rt.report_all_touched(iter).unwrap_or_exit(); rt.report_all_touched(iter)?;
} else { } else {
debug!("Linking internally: {:?} -> {:?}", from, entry); debug!("Linking internally: {:?} -> {:?}", from, entry);
let from_id = StoreId::new(PathBuf::from(from)).map_err_trace_exit_unwrap(); let from_id = StoreId::new(PathBuf::from(from))?;
let entr_id = StoreId::new(PathBuf::from(entry)).map_err_trace_exit_unwrap(); let entr_id = StoreId::new(PathBuf::from(entry))?;
if from_id == entr_id { if from_id == entr_id {
error!("Cannot link entry with itself. Exiting"); return Err(err_msg("Cannot link entry with itself. Exiting"))
::std::process::exit(1)
} }
let mut to_entry = match rt.store().get(entr_id).map_err_trace_exit_unwrap() { let mut to_entry = rt
Some(e) => e, .store()
None => { .get(entr_id)?
warn!("No 'to' entry: {}", entry); .ok_or_else(|| format_err!("No 'to' entry: {}", entry))?;
::std::process::exit(1)
},
};
from_entry
.add_link(&mut to_entry)
.map_err_trace_exit_unwrap();
rt.report_touched(to_entry.get_location()).unwrap_or_exit(); from_entry.add_link(&mut to_entry)?;
rt.report_touched(to_entry.get_location())?;
} }
info!("Ok: {} -> {}", from, entry); info!("Ok: {} -> {}", from, entry);
} }
rt.report_touched(from_entry.get_location()).unwrap_or_exit(); rt.report_touched(from_entry.get_location()).map_err(Error::from)
} }
fn remove_linking(rt: &Runtime) { fn remove_linking(rt: &Runtime) -> Result<()> {
let mut from = rt.cli() let mut from : FileLockEntry = rt.cli()
.subcommand_matches("remove") .subcommand_matches("remove")
.unwrap() // safe, we know there is an "remove" subcommand .unwrap() // safe, we know there is an "remove" subcommand
.value_of("from") .value_of("from")
.map(PathBuf::from) .map(PathBuf::from)
.map(|id| { .and_then(|id| rt.store().get(id).transpose())
rt.store() .ok_or_else(|| err_msg("No 'from' entry"))??;
.get(id)
.map_err_trace_exit_unwrap()
.ok_or_else(|| warn_exit("No 'from' entry", 1))
.unwrap() // safe by line above
})
.unwrap();
rt rt
.ids::<crate::ui::PathProvider>() .ids::<crate::ui::PathProvider>()?
.map_err_trace_exit_unwrap() .ok_or_else(|| err_msg("No ids supplied"))?
.unwrap_or_else(|| {
error!("No ids supplied");
::std::process::exit(1);
})
.into_iter() .into_iter()
.for_each(|id| match rt.store().get(id.clone()) { .map(|id| match rt.store().get(id.clone())? {
Err(e) => trace_error(&e), Some(mut to_entry) => {
Ok(Some(mut to_entry)) => { to_entry.remove_link(&mut from)?;
to_entry rt.report_touched(to_entry.get_location()).map_err(Error::from)
.remove_link(&mut from)
.map_err_trace_exit_unwrap();
rt.report_touched(to_entry.get_location()).unwrap_or_exit();
}, },
Ok(None) => {
None => {
// looks like this is not an entry, but a filesystem URI and therefor an // looks like this is not an entry, but a filesystem URI and therefor an
// external link...? // external link...?
if id.local().is_file() { if id.local().is_file() {
let pb = id.local().to_str().unwrap_or_else(|| { let pb = id.local().to_str().ok_or_else(|| format_err!("Not StoreId and not a Path: {}", id))?;
warn!("Not StoreId and not a Path: {}", id); let url = Url::parse(pb).map_err(|e| format_err!("Error parsing URL: {:?}", e))?;
::std::process::exit(1); from.remove_url(rt.store(), url)?;
});
let url = Url::parse(pb).unwrap_or_else(|e| {
error!("Error parsing URL: {:?}", e);
::std::process::exit(1);
});
from.remove_url(rt.store(), url).map_err_trace_exit_unwrap();
info!("Ok: {}", id); info!("Ok: {}", id);
Ok(())
} else { } else {
warn!("Entry not found: {:?}", id); Err(format_err!("Entry not found: {:?}", id))
} }
} }
}); })
.collect::<Result<Vec<_>>>()?;
rt.report_touched(from.get_location()).unwrap_or_exit(); rt.report_touched(from.get_location()).map_err(Error::from)
} }
fn unlink(rt: &Runtime) { fn unlink(rt: &Runtime) -> Result<()> {
rt rt
.ids::<crate::ui::PathProvider>() .ids::<crate::ui::PathProvider>()?
.map_err_trace_exit_unwrap() .ok_or_else(|| err_msg("No ids supplied"))?
.unwrap_or_else(|| {
error!("No ids supplied");
::std::process::exit(1);
})
.into_iter() .into_iter()
.for_each(|id| { .map(|id| {
rt.store() rt.store()
.get(id.clone()) .get(id.clone())?
.map_err_trace_exit_unwrap() .ok_or_else(|| format_err!("No entry for {}", id))?
.unwrap_or_else(|| { .unlink(rt.store())?;
warn!("No entry for {}", id);
::std::process::exit(1)
})
.unlink(rt.store())
.map_err_trace_exit_unwrap();
rt.report_touched(&id).unwrap_or_exit(); rt.report_touched(&id).map_err(Error::from)
}); })
.collect()
} }
fn list_linkings(rt: &Runtime) { fn list_linkings(rt: &Runtime) -> Result<()> {
let cmd = rt.cli() let cmd = rt.cli()
.subcommand_matches("list") .subcommand_matches("list")
.unwrap(); // safed by clap .unwrap(); // safed by clap
@ -304,70 +244,50 @@ fn list_linkings(rt: &Runtime) {
let mut tab = ::prettytable::Table::new(); let mut tab = ::prettytable::Table::new();
tab.set_titles(row!["#", "Link"]); tab.set_titles(row!["#", "Link"]);
rt rt.ids::<crate::ui::PathProvider>()?
.ids::<crate::ui::PathProvider>() .ok_or_else(|| err_msg("No ids supplied"))?
.map_err_trace_exit_unwrap()
.unwrap_or_else(|| {
error!("No ids supplied");
::std::process::exit(1);
})
.into_iter() .into_iter()
.for_each(|id| { .map(|id| {
match rt.store().get(id.clone()) { let entry = rt.store().get(id.clone())?.ok_or_else(|| format_err!("Not found: {}", id))?;
Ok(Some(entry)) => {
for (i, link) in entry.links().map_err_trace_exit_unwrap().enumerate() { for (i, link) in entry.links()?.enumerate() {
let link = link let link = link.to_str()?;
.to_str()
.map_warn_err(|e| format!("Failed to convert StoreId to string: {:?}", e))
.ok();
if let Some(link) = link {
if list_plain { if list_plain {
writeln!(rt.stdout(), "{: <3}: {}", i, link) writeln!(rt.stdout(), "{: <3}: {}", i, link)?;
.to_exit_code()
.unwrap_or_exit();
} else { } else {
tab.add_row(row![i, link]); tab.add_row(row![i, link]);
} }
} }
}
if list_externals { if list_externals {
entry.get_urls(rt.store()) entry.get_urls(rt.store())?
.map_err_trace_exit_unwrap()
.enumerate() .enumerate()
.for_each(|(i, link)| { .map(|(i, link)| {
let link = link let link = link?.into_string();
.map_err_trace_exit_unwrap()
.into_string();
if list_plain { if list_plain {
writeln!(rt.stdout(), "{: <3}: {}", i, link) writeln!(rt.stdout(), "{: <3}: {}", i, link)?;
.to_exit_code()
.unwrap_or_exit();
} else { } else {
tab.add_row(row![i, link]); tab.add_row(row![i, link]);
} }
Ok(())
}) })
.collect::<Result<Vec<_>>>()?;
} }
rt.report_touched(entry.get_location()).unwrap_or_exit(); rt.report_touched(entry.get_location()).map_err(Error::from)
})
}, .collect::<Result<Vec<_>>>()?;
Ok(None) => warn!("Not found: {}", id),
Err(e) => trace_error(&e),
}
rt.report_touched(&id).unwrap_or_exit();
});
if !list_plain { if !list_plain {
let out = rt.stdout(); let out = rt.stdout();
let mut lock = out.lock(); let mut lock = out.lock();
tab.print(&mut lock) tab.print(&mut lock)?;
.to_exit_code()
.unwrap_or_exit();
} }
Ok(())
} }
#[cfg(test)] #[cfg(test)]
@ -449,7 +369,7 @@ mod tests {
debug!("Entries created"); debug!("Entries created");
link_from_to(&rt, "test1", vec!["test2"].into_iter()); link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();
debug!("Linking done"); debug!("Linking done");
@ -480,7 +400,7 @@ mod tests {
debug!("Test entries created"); debug!("Test entries created");
link_from_to(&rt, "test1", vec!["test2"].into_iter()); link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();
debug!("Linking done"); debug!("Linking done");
@ -509,8 +429,8 @@ mod tests {
debug!("Test entries created"); debug!("Test entries created");
link_from_to(&rt, "test1", vec!["test2"].into_iter()); link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();
link_from_to(&rt, "test1", vec!["test2"].into_iter()); link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();
debug!("Linking done"); debug!("Linking done");
@ -540,8 +460,8 @@ mod tests {
debug!("Test entries created"); debug!("Test entries created");
link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter()); link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter()).unwrap();
link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter()); link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter()).unwrap();
debug!("Linking done"); debug!("Linking done");
@ -576,14 +496,14 @@ mod tests {
debug!("Test entries created"); debug!("Test entries created");
link_from_to(&rt, "test1", vec!["test2"].into_iter()); link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();
debug!("Linking done"); debug!("Linking done");
let rt = reset_test_runtime(vec!["remove", "test1", "test2"], rt) let rt = reset_test_runtime(vec!["remove", "test1", "test2"], rt)
.unwrap(); .unwrap();
remove_linking(&rt); remove_linking(&rt).unwrap();
debug!("Linking removed"); debug!("Linking removed");
@ -613,14 +533,14 @@ mod tests {
debug!("Test entries created"); debug!("Test entries created");
link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter()); link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter()).unwrap();
debug!("linking done"); debug!("linking done");
let rt = reset_test_runtime(vec!["remove", "test1", "test2", "test3"], rt) let rt = reset_test_runtime(vec!["remove", "test1", "test2", "test3"], rt)
.unwrap(); .unwrap();
remove_linking(&rt); remove_linking(&rt).unwrap();
debug!("linking removed"); debug!("linking removed");