libimagrt: Move from error-chain to failure

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
Matthias Beyer 2018-10-30 18:40:50 +01:00
parent 09e8619cf5
commit 8236e73402
6 changed files with 135 additions and 258 deletions

View file

@ -25,10 +25,10 @@ toml = "0.4"
xdg-basedir = "1.0" xdg-basedir = "1.0"
itertools = "0.7" itertools = "0.7"
ansi_term = "0.11" ansi_term = "0.11"
is-match = "0.1" toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
toml-query = "0.7"
error-chain = "0.12"
atty = "0.2" atty = "0.2"
failure = "0.1"
failure_derive = "0.1"
libimagstore = { version = "0.9.0", path = "../../../lib/core/libimagstore" } libimagstore = { version = "0.9.0", path = "../../../lib/core/libimagstore" }
libimagerror = { version = "0.9.0", path = "../../../lib/core/libimagerror" } libimagerror = { version = "0.9.0", path = "../../../lib/core/libimagerror" }

View file

@ -21,20 +21,19 @@ use std::path::PathBuf;
use toml::Value; use toml::Value;
use clap::App; use clap::App;
use failure::ResultExt;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use error::RuntimeError as RE; use libimagerror::errors::ErrorMsg as EM;
use error::RuntimeErrorKind as REK;
use error::Result;
use error::ResultExt;
/// Get a new configuration object. /// Get a new configuration object.
/// ///
/// The passed runtimepath is used for searching the configuration file, whereas several file /// The passed runtimepath is used for searching the configuration file, whereas several file
/// names are tested. If that does not work, the home directory and the XDG basedir are tested /// names are tested. If that does not work, the home directory and the XDG basedir are tested
/// with all variants. /// with all variants.
/// pub fn fetch_config(searchpath: &PathBuf) -> Result<Option<Value>> {
/// If that doesn't work either, an error is returned.
pub fn fetch_config(searchpath: &PathBuf) -> Result<Value> {
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
@ -65,7 +64,7 @@ pub fn fetch_config(searchpath: &PathBuf) -> Result<Value> {
.unwrap_or(vec![]), .unwrap_or(vec![]),
]; ];
Itertools::flatten(vals.iter()) let config = Itertools::flatten(vals.iter())
.filter(|path| path.exists() && path.is_file()) .filter(|path| path.exists() && path.is_file())
.filter_map(|path| { .filter_map(|path| {
let content = { let content = {
@ -90,13 +89,14 @@ pub fn fetch_config(searchpath: &PathBuf) -> Result<Value> {
.unwrap_or_else(|| String::from("Line unknown, Column unknown")); .unwrap_or_else(|| String::from("Line unknown, Column unknown"));
let _ = write!(stderr(), "Config file parser error at {}", line_col); let _ = write!(stderr(), "Config file parser error at {}", line_col);
let e : RE = RE::from(e); let e = Error::from(EM::TomlDeserError);
trace_error(&e); trace_error(&e);
None None
}) })
}) })
.nth(0) .nth(0);
.ok_or(RE::from_kind(REK::ConfigNoConfigFileFound))
Ok(config)
} }
/// Override the configuration. /// Override the configuration.
@ -120,16 +120,16 @@ pub fn override_config(val: &mut Value, v: Vec<String>) -> Result<()> {
.map(|(k, v)| { .map(|(k, v)| {
let value = val let value = val
.read(&k) .read(&k)
.chain_err(|| REK::ConfigTOMLParserError)? .context(EM::TomlQueryError)?
.ok_or(RE::from_kind(REK::ConfigOverrideKeyNotAvailable))?; .ok_or_else(|| Error::from(err_msg("Confit parser error")))?;
into_value(value, v) into_value(value, v)
.map(|v| info!("Successfully overridden: {} = {}", k, v)) .map(|v| info!("Successfully overridden: {} = {}", k, v))
.ok_or_else(|| RE::from_kind(REK::ConfigOverrideTypeNotMatching)) .ok_or_else(|| Error::from(err_msg("Config override type not matching")))
}); });
for elem in iter { for elem in iter {
let _ = try!(elem.chain_err(|| REK::ConfigOverrideError)); let _ = elem.context(err_msg("Config override error"))?;
} }
Ok(()) Ok(())

View file

@ -1,125 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
error_chain! {
types {
RuntimeError, RuntimeErrorKind, ResultExt, Result;
}
foreign_links {
IO(::std::io::Error);
TomlDeError(::toml::de::Error);
TomlQueryError(::toml_query::error::Error);
HandlebarsTemplateError(::handlebars::TemplateError);
}
errors {
Instantiate {
description("Could not instantiate")
display("Could not instantiate")
}
IOError {
description("IO Error")
display("IO Error")
}
ProcessExitFailure {
description("Process exited with failure")
display("Process exited with failure")
}
IOLogFileOpenError {
description("IO Error: Could not open logfile")
display("IO Error: Could not open logfile")
}
ConfigTypeError(path: String, should_be_type: &'static str) {
description("Error while reading the configuration: Type Error")
display("Type Error: '{}' should be '{}'", path, should_be_type)
}
GlobalLogLevelConfigMissing {
description("Global config 'imag.logging.level' missing")
display("Global config 'imag.logging.level' missing")
}
GlobalDestinationConfigMissing {
description("Global config 'imag.logging.destinations' missing")
display("Global config 'imag.logging.destinations' missing")
}
InvalidLogLevelSpec {
description("Invalid log level specification: Only 'trace', 'debug', 'info', 'warn', 'error' are allowed")
display("Invalid log level specification: Only 'trace', 'debug', 'info', 'warn', 'error' are allowed")
}
ConfigMissingLoggingFormatTrace {
description("Missing config for logging format for trace logging")
display("Missing config for logging format for trace logging")
}
ConfigMissingLoggingFormatDebug {
description("Missing config for logging format for debug logging")
display("Missing config for logging format for debug logging")
}
ConfigMissingLoggingFormatInfo {
description("Missing config for logging format for info logging")
display("Missing config for logging format for info logging")
}
ConfigMissingLoggingFormatWarn {
description("Missing config for logging format for warn logging")
display("Missing config for logging format for warn logging")
}
ConfigMissingLoggingFormatError {
description("Missing config for logging format for error logging")
display("Missing config for logging format for error logging")
}
ConfigTOMLParserError {
description("Configuration: TOML Parsing error")
display("Configuration: TOML Parsing error")
}
ConfigNoConfigFileFound {
description("Configuration: No config file found")
display("Configuration: No config file found")
}
ConfigOverrideError {
description("Configuration: Config override error")
display("Configuration: Config override error")
}
ConfigOverrideKeyNotAvailable {
description("Configuration: Key not available")
display("Configuration: Key not available")
}
ConfigOverrideTypeNotMatching {
description("Configuration: Configuration Type not matching")
display("Configuration: Configuration Type not matching")
}
}
}

View file

@ -36,17 +36,16 @@
)] )]
#[macro_use] extern crate log; #[macro_use] extern crate log;
#[macro_use] extern crate error_chain;
extern crate itertools; extern crate itertools;
#[cfg(unix)] extern crate xdg_basedir; #[cfg(unix)] extern crate xdg_basedir;
extern crate env_logger; extern crate env_logger;
extern crate ansi_term; extern crate ansi_term;
extern crate handlebars; extern crate handlebars;
#[macro_use] extern crate failure;
extern crate clap; extern crate clap;
extern crate toml; extern crate toml;
extern crate toml_query; extern crate toml_query;
#[macro_use] extern crate is_match;
extern crate atty; extern crate atty;
extern crate libimagstore; extern crate libimagstore;
@ -54,7 +53,6 @@ extern crate libimagutil;
extern crate libimagerror; extern crate libimagerror;
extern crate libimaginteraction; extern crate libimaginteraction;
pub mod error;
pub mod configuration; pub mod configuration;
pub mod logger; pub mod logger;
pub mod io; pub mod io;

View file

@ -24,11 +24,12 @@ use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use std::ops::Deref; use std::ops::Deref;
use error::RuntimeErrorKind as EK;
use error::RuntimeError as RE;
use error::ResultExt;
use runtime::Runtime; use runtime::Runtime;
use failure::ResultExt;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use clap::ArgMatches; use clap::ArgMatches;
use log::{Log, Level, Record, Metadata}; use log::{Log, Level, Record, Metadata};
use toml::Value; use toml::Value;
@ -36,10 +37,10 @@ use toml_query::read::TomlValueReadExt;
use toml_query::read::TomlValueReadTypeExt; use toml_query::read::TomlValueReadTypeExt;
use handlebars::Handlebars; use handlebars::Handlebars;
type ModuleName = String; use libimagerror::errors::ErrorMsg as EM;
type Result<T> = ::std::result::Result<T, RE>;
type ModuleName = String;
#[derive(Debug)]
enum LogDestination { enum LogDestination {
Stderr, Stderr,
File(Arc<Mutex<::std::fs::File>>), File(Arc<Mutex<::std::fs::File>>),
@ -51,7 +52,6 @@ impl Default for LogDestination {
} }
} }
#[derive(Debug)]
struct ModuleSettings { struct ModuleSettings {
enabled: bool, enabled: bool,
level: Option<Level>, level: Option<Level>,
@ -89,32 +89,39 @@ impl ImagLogger {
{ {
let fmt = aggregate_global_format_trace(config)?; let fmt = aggregate_global_format_trace(config)?;
handlebars.register_template_string("TRACE", fmt)?; // name must be uppercase handlebars.register_template_string("TRACE", fmt)
.map_err(Error::from)
.context(err_msg("Handlebars template error"))?; // name must be uppercase
} }
{ {
let fmt = aggregate_global_format_debug(config)?; let fmt = aggregate_global_format_debug(config)?;
handlebars.register_template_string("DEBUG", fmt)?; // name must be uppercase handlebars.register_template_string("DEBUG", fmt)
.map_err(Error::from)
.context(err_msg("Handlebars template error"))?; // name must be uppercase
} }
{ {
let fmt = aggregate_global_format_info(config)?; let fmt = aggregate_global_format_info(config)?;
handlebars.register_template_string("INFO", fmt)?; // name must be uppercase handlebars.register_template_string("INFO", fmt)
.map_err(Error::from)
.context(err_msg("Handlebars template error"))?; // name must be uppercase
} }
{ {
let fmt = aggregate_global_format_warn(config)?; let fmt = aggregate_global_format_warn(config)?;
handlebars.register_template_string("WARN", fmt)?; // name must be uppercase handlebars.register_template_string("WARN", fmt)
.map_err(Error::from)
.context(err_msg("Handlebars template error"))?; // name must be uppercase
} }
{ {
let fmt = aggregate_global_format_error(config)?; let fmt = aggregate_global_format_error(config)?;
handlebars.register_template_string("ERROR", fmt)?; // name must be uppercase handlebars.register_template_string("ERROR", fmt)
.map_err(Error::from)
.context(err_msg("Handlebars template error"))?; // name must be uppercase
} }
let module_settings = aggregate_module_settings(matches, config)?;
eprintln!("Logging: {:?}", module_settings);
Ok(ImagLogger { Ok(ImagLogger {
global_loglevel : aggregate_global_loglevel(matches, config)?, global_loglevel : aggregate_global_loglevel(matches, config)?,
global_destinations : aggregate_global_destinations(matches, config)?, global_destinations : aggregate_global_destinations(matches, config)?,
module_settings : module_settings, module_settings : aggregate_module_settings(matches, config)?,
handlebars : handlebars, handlebars : handlebars,
}) })
} }
@ -179,35 +186,28 @@ impl Log for ImagLogger {
self.module_settings self.module_settings
.get(record_target) .get(record_target)
.map(|module_setting| { .map(|module_setting| {
// We have a module setting some
// * Check whether logging is enabled for this module and
// * check whether the module logging level is >= or, if there is no module logging
// level,
// * check whether the global logging level is >= the record level.
let set = module_setting.enabled && let set = module_setting.enabled &&
module_setting.level.unwrap_or(self.global_loglevel) >= record.level(); module_setting.level.unwrap_or(self.global_loglevel) >= record.level();
if set { // if we want to log from a setting standpoint if set {
// get the destinations for the module and log to all of them
module_setting.destinations.as_ref().map(|destinations| for d in destinations { module_setting.destinations.as_ref().map(|destinations| for d in destinations {
let _ = log_to_destination(&d); // ignore errors, because what else? // If there's an error, we cannot do anything, can we?
let _ = log_to_destination(&d);
}); });
// after that, log to the global destinations as well
for d in self.global_destinations.iter() { for d in self.global_destinations.iter() {
let _ = log_to_destination(&d); // ignore errors, because what else? // If there's an error, we cannot do anything, can we?
let _ = log_to_destination(&d);
} }
} }
}) })
// if we do not have a setting for the record target
.unwrap_or_else(|| { .unwrap_or_else(|| {
if self.global_loglevel >= record.level() { // if logging is enabled for that level if self.global_loglevel >= record.level() {
self.global_destinations // Yes, we log
.iter() for d in self.global_destinations.iter() {
.for_each(|d| { // log to all global destinations // If there's an error, we cannot do anything, can we?
let _ = log_to_destination(&d); // ignore errors, because what else? let _ = log_to_destination(&d);
}); }
} }
}); });
} }
@ -220,7 +220,7 @@ fn match_log_level_str(s: &str) -> Result<Level> {
"info" => Ok(Level::Info), "info" => Ok(Level::Info),
"warn" => Ok(Level::Warn), "warn" => Ok(Level::Warn),
"error" => Ok(Level::Error), "error" => Ok(Level::Error),
_ => return Err(RE::from_kind(EK::InvalidLogLevelSpec)), lvl => return Err(format_err!("Invalid logging level: {}", lvl)),
} }
} }
@ -243,8 +243,10 @@ fn aggregate_global_loglevel(matches: &ArgMatches, config: Option<&Value>) -> Re
if let Some(cfg) = config { if let Some(cfg) = config {
let cfg_loglevel = cfg let cfg_loglevel = cfg
.read_string("imag.logging.level")? .read_string("imag.logging.level")
.ok_or(RE::from_kind(EK::GlobalLogLevelConfigMissing)) .map_err(Error::from)
.context(EM::TomlQueryError)?
.ok_or(err_msg("Global log level config missing"))
.and_then(|s| match_log_level_str(&s))?; .and_then(|s| match_log_level_str(&s))?;
if let Some(cli_loglevel) = get_arg_loglevel(matches)? { if let Some(cli_loglevel) = get_arg_loglevel(matches)? {
@ -273,7 +275,9 @@ fn translate_destination(raw: &str) -> Result<LogDestination> {
.map(Mutex::new) .map(Mutex::new)
.map(Arc::new) .map(Arc::new)
.map(LogDestination::File) .map(LogDestination::File)
.chain_err(|| EK::IOLogFileOpenError) .map_err(Error::from)
.context(EM::IO)
.map_err(Error::from)
} }
} }
} }
@ -285,9 +289,9 @@ fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
acc.and_then(|mut v| { acc.and_then(|mut v| {
let dest = val.as_str() let dest = val.as_str()
.ok_or_else(|| { .ok_or_else(|| {
let path = "imag.logging.modules.<mod>.destinations".to_owned(); let path = "imag.logging.modules.<mod>.destinations";
let ty = "Array<String>"; let ty = "Array<String>";
RE::from_kind(EK::ConfigTypeError(path, ty)) Error::from(format_err!("Type error at {}, expected {}", path, ty))
}) })
.and_then(translate_destination)?; .and_then(translate_destination)?;
v.push(dest); v.push(dest);
@ -299,16 +303,18 @@ fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>) fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
-> Result<Vec<LogDestination>> -> Result<Vec<LogDestination>>
{ {
let config_log_dest_path = "imag.logging.destinations";
match config { match config {
Some(cfg) => cfg Some(cfg) => cfg
.read(&config_log_dest_path)? .read("imag.logging.destinations")
.ok_or_else(|| RE::from_kind(EK::GlobalDestinationConfigMissing))? .map_err(Error::from)
.context(EM::TomlQueryError)?
.ok_or_else(|| err_msg("Global log destination config missing"))?
.as_array() .as_array()
.ok_or_else(|| { .ok_or_else(|| {
let path = config_log_dest_path.to_owned(); let path = "imag.logging.destinations";
let ty = "Array"; let ty = "Array";
RE::from_kind(EK::ConfigTypeError(path, ty)) Error::from(format_err!("Type error at {}, expected {}", path, ty))
}) })
.and_then(translate_destinations), .and_then(translate_destinations),
None => { None => {
@ -330,10 +336,12 @@ fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
} }
macro_rules! aggregate_global_format { macro_rules! aggregate_global_format {
($read_str:expr, $error_kind_if_missing:expr, $config:expr) => { ($read_str:expr, $error_msg_if_missing:expr, $config:expr) => {
try!($config.ok_or(RE::from_kind($error_kind_if_missing))) try!($config.ok_or_else(|| Error::from(err_msg($error_msg_if_missing))))
.read_string($read_str)? .read_string($read_str)
.ok_or_else(|| RE::from_kind($error_kind_if_missing)) .map_err(Error::from)
.context(EM::TomlQueryError)?
.ok_or_else(|| Error::from(err_msg($error_msg_if_missing)))
}; };
} }
@ -341,43 +349,43 @@ fn aggregate_global_format_trace(config: Option<&Value>)
-> Result<String> -> Result<String>
{ {
aggregate_global_format!("imag.logging.format.trace", aggregate_global_format!("imag.logging.format.trace",
EK::ConfigMissingLoggingFormatTrace, "Config missing: Logging format: Trace",
config) config)
} }
fn aggregate_global_format_debug(config: Option<&Value>) fn aggregate_global_format_debug(config: Option<&Value>)
-> Result<String> -> Result<String>
{ {
aggregate_global_format!("imag.logging.format.debug", aggregate_global_format!("imag.logging.format.debug",
EK::ConfigMissingLoggingFormatDebug, "Config missing: Logging format: Debug",
config) config)
} }
fn aggregate_global_format_info(config: Option<&Value>) fn aggregate_global_format_info(config: Option<&Value>)
-> Result<String> -> Result<String>
{ {
aggregate_global_format!("imag.logging.format.info", aggregate_global_format!("imag.logging.format.info",
EK::ConfigMissingLoggingFormatInfo, "Config missing: Logging format: Info",
config) config)
} }
fn aggregate_global_format_warn(config: Option<&Value>) fn aggregate_global_format_warn(config: Option<&Value>)
-> Result<String> -> Result<String>
{ {
aggregate_global_format!("imag.logging.format.warn", aggregate_global_format!("imag.logging.format.warn",
EK::ConfigMissingLoggingFormatWarn, "Config missing: Logging format: Warn",
config) config)
} }
fn aggregate_global_format_error(config: Option<&Value>) fn aggregate_global_format_error(config: Option<&Value>)
-> Result<String> -> Result<String>
{ {
aggregate_global_format!("imag.logging.format.error", aggregate_global_format!("imag.logging.format.error",
EK::ConfigMissingLoggingFormatError, "Config missing: Logging format: Error",
config) config)
} }
fn aggregate_module_settings(matches: &ArgMatches, config: Option<&Value>) fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
-> Result<BTreeMap<ModuleName, ModuleSettings>> -> Result<BTreeMap<ModuleName, ModuleSettings>>
{ {
// Helper macro to return the error from Some(Err(_)) and map everything else to an // Helper macro to return the error from Some(Err(_)) and map everything else to an
@ -393,44 +401,43 @@ fn aggregate_module_settings(matches: &ArgMatches, config: Option<&Value>)
}; };
match config { match config {
Some(cfg) => match cfg.read("imag.logging.modules") { Some(cfg) => match cfg.read("imag.logging.modules").map_err(Error::from) {
Ok(Some(&Value::Table(ref t))) => { Ok(Some(&Value::Table(ref t))) => {
// translate the module settings from the table `t` // translate the module settings from the table `t`
let mut settings = BTreeMap::new(); let mut settings = BTreeMap::new();
for (module_name, v) in t { for (module_name, v) in t {
let destinations = inner_try! { let destinations = inner_try! {
v.read("destinations")? v.read("destinations")
.map_err(Error::from)
.context(EM::TomlQueryError)?
.map(|val| { .map(|val| {
val.as_array() val.as_array()
.ok_or_else(|| { .ok_or_else(|| {
let path = "imag.logging.modules.<mod>.destinations".to_owned(); let path = "imag.logging.modules.<mod>.destinations";
let ty = "Array"; let ty = "Array";
RE::from_kind(EK::ConfigTypeError(path, ty)) Error::from(format_err!("Type error at {}, expected {}", path, ty))
}) })
.and_then(translate_destinations) .and_then(translate_destinations)
}) })
}; };
let level = inner_try! {
let (pre_enabled, level) = if matches.is_present(Runtime::arg_debugging_name()) { v.read_string("level")
(true, Some(Level::Debug)) .map_err(Error::from)
} else { .context(EM::TomlQueryError)?
let level = inner_try! { .map(|s| match_log_level_str(&s))
v.read_string("level")?.map(|s| match_log_level_str(&s))
};
(false, level)
}; };
let enabled = pre_enabled || let enabled = v.read("enabled")
v.read("enabled")? .map_err(Error::from)
.map(|v| v.as_bool().unwrap_or(false)) .context(EM::TomlQueryError)?
.ok_or_else(|| { .map(|v| v.as_bool().unwrap_or(false))
let path = "imag.logging.modules.<mod>.enabled".to_owned(); .ok_or_else(|| {
let ty = "Boolean"; let path = "imag.logging.modules.<mod>.enabled";
RE::from_kind(EK::ConfigTypeError(path, ty)) let ty = "Boolean";
})?; Error::from(format_err!("Type error at {}, expected {}", path, ty))
})?;
let module_settings = ModuleSettings { let module_settings = ModuleSettings {
enabled: enabled, enabled: enabled,
@ -445,15 +452,15 @@ fn aggregate_module_settings(matches: &ArgMatches, config: Option<&Value>)
Ok(settings) Ok(settings)
}, },
Ok(Some(_)) => { Ok(Some(_)) => {
let path = "imag.logging.modules".to_owned(); let path = "imag.logging.modules";
let ty = "Table"; let ty = "Table";
Err(RE::from_kind(EK::ConfigTypeError(path, ty))) Err(Error::from(format_err!("Type error at {}, expected {}", path, ty)))
}, },
Ok(None) => { Ok(None) => {
// No modules configured. This is okay! // No modules configured. This is okay!
Ok(BTreeMap::new()) Ok(BTreeMap::new())
}, },
Err(e) => Err(e).map_err(From::from), Err(e) => Err(e).context(EM::TomlQueryError).map_err(Error::from),
}, },
None => { None => {
write!(stderr(), "No Configuration.").ok(); write!(stderr(), "No Configuration.").ok();

View file

@ -30,14 +30,16 @@ use toml::Value;
use toml_query::read::TomlValueReadExt; use toml_query::read::TomlValueReadExt;
use clap::{Arg, ArgMatches}; use clap::{Arg, ArgMatches};
use failure::ResultExt;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use configuration::{fetch_config, override_config, InternalConfiguration}; use configuration::{fetch_config, override_config, InternalConfiguration};
use error::RuntimeError;
use error::RuntimeErrorKind;
use error::ResultExt;
use logger::ImagLogger; use logger::ImagLogger;
use io::OutputProxy; use io::OutputProxy;
use libimagerror::errors::ErrorMsg as EM;
use libimagerror::trace::*; use libimagerror::trace::*;
use libimagstore::store::Store; use libimagstore::store::Store;
use libimagstore::file_abstraction::InMemoryFileAbstraction; use libimagstore::file_abstraction::InMemoryFileAbstraction;
@ -62,7 +64,7 @@ impl<'a> Runtime<'a> {
/// and builds the Runtime object with it. /// and builds the Runtime object with it.
/// ///
/// The cli_app object should be initially build with the ::get_default_cli_builder() function. /// The cli_app object should be initially build with the ::get_default_cli_builder() function.
pub fn new<C>(cli_app: C) -> Result<Runtime<'a>, RuntimeError> pub fn new<C>(cli_app: C) -> Result<Runtime<'a>>
where C: Clone + CliSpec<'a> + InternalConfiguration where C: Clone + CliSpec<'a> + InternalConfiguration
{ {
use libimagerror::trace::trace_error; use libimagerror::trace::trace_error;
@ -76,17 +78,15 @@ impl<'a> Runtime<'a> {
debug!("Config path = {:?}", configpath); debug!("Config path = {:?}", configpath);
let config = match fetch_config(&configpath) { let config = match fetch_config(&configpath)? {
Err(e) => if !is_match!(e.kind(), &RuntimeErrorKind::ConfigNoConfigFileFound) { None => {
return Err(e).chain_err(|| RuntimeErrorKind::Instantiate); return Err(err_msg("No configuration file found"))
} else { .context(err_msg("Maybe try to use 'imag-init' to initialize imag?"))
eprintln!("No config file found."); .context(err_msg("Continuing without configuration file"))
eprintln!("Maybe try to use 'imag-init' to initialize imag?"); .context(err_msg("Cannot instantiate runtime"))
eprintln!("Continuing without configuration file"); .map_err(Error::from);
None
}, },
Some(mut config) => {
Ok(mut config) => {
if let Err(e) = override_config(&mut config, get_override_specs(&matches)) { if let Err(e) = override_config(&mut config, get_override_specs(&matches)) {
error!("Could not apply config overrides"); error!("Could not apply config overrides");
trace_error(&e); trace_error(&e);
@ -102,16 +102,14 @@ impl<'a> Runtime<'a> {
} }
/// Builds the Runtime object using the given `config`. /// Builds the Runtime object using the given `config`.
pub fn with_configuration<C>(cli_app: C, config: Option<Value>) pub fn with_configuration<C>(cli_app: C, config: Option<Value>) -> Result<Runtime<'a>>
-> Result<Runtime<'a>, RuntimeError>
where C: Clone + CliSpec<'a> + InternalConfiguration where C: Clone + CliSpec<'a> + InternalConfiguration
{ {
let matches = cli_app.clone().matches(); let matches = cli_app.clone().matches();
Runtime::_new(cli_app, matches, config) Runtime::_new(cli_app, matches, config)
} }
fn _new<C>(cli_app: C, matches: ArgMatches<'a>, config: Option<Value>) fn _new<C>(cli_app: C, matches: ArgMatches<'a>, config: Option<Value>) -> Result<Runtime<'a>>
-> Result<Runtime<'a>, RuntimeError>
where C: Clone + CliSpec<'a> + InternalConfiguration where C: Clone + CliSpec<'a> + InternalConfiguration
{ {
if cli_app.enable_logging() { if cli_app.enable_logging() {
@ -146,7 +144,8 @@ impl<'a> Runtime<'a> {
store: store, store: store,
} }
}) })
.chain_err(|| RuntimeErrorKind::Instantiate) .context(err_msg("Cannot instantiate runtime"))
.map_err(Error::from)
} }
/// ///
@ -379,7 +378,7 @@ impl<'a> Runtime<'a> {
} }
/// Get a editor command object which can be called to open the $EDITOR /// Get a editor command object which can be called to open the $EDITOR
pub fn editor(&self) -> Result<Option<Command>, RuntimeError> { pub fn editor(&self) -> Result<Option<Command>> {
self.cli() self.cli()
.value_of("editor") .value_of("editor")
.map(String::from) .map(String::from)
@ -391,7 +390,7 @@ impl<'a> Runtime<'a> {
}) })
}) })
.or(env::var("EDITOR").ok()) .or(env::var("EDITOR").ok())
.ok_or_else(|| RuntimeErrorKind::IOError.into()) .ok_or_else(|| Error::from(EM::IO))
.map_dbg(|s| format!("Editing with '{}'", s)) .map_dbg(|s| format!("Editing with '{}'", s))
.and_then(|s| { .and_then(|s| {
let mut split = s.split_whitespace(); let mut split = s.split_whitespace();
@ -401,7 +400,7 @@ impl<'a> Runtime<'a> {
} }
let mut c = Command::new(command.unwrap()); // secured above let mut c = Command::new(command.unwrap()); // secured above
c.args(split); c.args(split);
c.stdin(::std::fs::File::open("/dev/tty")?); c.stdin(::std::fs::File::open("/dev/tty").context(EM::IO)?);
c.stderr(::std::process::Stdio::inherit()); c.stderr(::std::process::Stdio::inherit());
Ok(Some(c)) Ok(Some(c))
}) })
@ -442,8 +441,6 @@ impl<'a> Runtime<'a> {
/// # Return value /// # Return value
/// ///
/// On success, the exit status object of the `Command` invocation is returned. /// On success, the exit status object of the `Command` invocation is returned.
/// On Error, a RuntimeError object is returned. This is also the case if writing the error
/// message does not work.
/// ///
/// # Details /// # Details
/// ///
@ -457,7 +454,7 @@ impl<'a> Runtime<'a> {
command: S, command: S,
subcommand: S, subcommand: S,
args: &ArgMatches) args: &ArgMatches)
-> Result<::std::process::ExitStatus, RuntimeError> -> Result<::std::process::ExitStatus>
{ {
use std::io::Write; use std::io::Write;
use std::io::ErrorKind; use std::io::ErrorKind;
@ -465,8 +462,7 @@ impl<'a> Runtime<'a> {
let rtp_str = self.rtp() let rtp_str = self.rtp()
.to_str() .to_str()
.map(String::from) .map(String::from)
.ok_or(RuntimeErrorKind::IOError) .ok_or_else(|| Error::from(EM::IO))?;
.map_err(RuntimeError::from_kind)?;
let command = format!("{}-{}", command.as_ref(), subcommand.as_ref()); let command = format!("{}-{}", command.as_ref(), subcommand.as_ref());
@ -497,7 +493,8 @@ impl<'a> Runtime<'a> {
}, },
_ => e, _ => e,
}) })
.map_err(RuntimeError::from) .context(EM::IO)
.map_err(Error::from)
} }
} }