Add new Runtime
constructor
that accepts a Configuration argument; removed `GetConfiguration` trait and all the previous changes needed for it
This commit is contained in:
parent
9a79232446
commit
6ded27c6ea
4 changed files with 155 additions and 181 deletions
|
@ -350,14 +350,11 @@ mod tests {
|
||||||
use libimagrt::spec::CliSpec;
|
use libimagrt::spec::CliSpec;
|
||||||
use libimagrt::runtime::Runtime;
|
use libimagrt::runtime::Runtime;
|
||||||
use libimagrt::error::RuntimeError;
|
use libimagrt::error::RuntimeError;
|
||||||
use libimagrt::configuration::{Configuration, GetConfiguration, InternalConfiguration,
|
use libimagrt::configuration::{Configuration, InternalConfiguration};
|
||||||
Result as ConfigResult, ConfigErrorKind};
|
|
||||||
use libimagrt::configuration::error::MapErrInto;
|
|
||||||
|
|
||||||
use libimagstore::storeid::StoreId;
|
use libimagstore::storeid::StoreId;
|
||||||
use libimagstore::store::{Result as StoreResult, FileLockEntry};
|
use libimagstore::store::{Result as StoreResult, FileLockEntry};
|
||||||
|
|
||||||
static TOML_CONFIG_FILE: &[u8] = include_bytes!("../test_config.toml");
|
|
||||||
static DEFAULT_ENTRY: &str = "\
|
static DEFAULT_ENTRY: &str = "\
|
||||||
---\
|
---\
|
||||||
[imag]\
|
[imag]\
|
||||||
|
@ -392,20 +389,18 @@ version = \"0.3.0\"\
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> GetConfiguration for MockLinkApp<'a> {
|
|
||||||
fn get_configuration(&self, _: &PathBuf) -> ConfigResult<Configuration> {
|
|
||||||
::toml::de::from_slice(TOML_CONFIG_FILE)
|
|
||||||
.map_err_into(ConfigErrorKind::TOMLParserError)
|
|
||||||
.map(Configuration::new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> InternalConfiguration for MockLinkApp<'a> {
|
impl<'a> InternalConfiguration for MockLinkApp<'a> {
|
||||||
fn enable_logging(&self) -> bool {
|
fn enable_logging(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_test_config() -> Option<Configuration> {
|
||||||
|
::toml::de::from_str("[store]\nimplicit-create=true")
|
||||||
|
.map(Configuration::with_value)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_test_runtime<'a>(mut args: Vec<&'static str>) -> Result<Runtime<'a>, RuntimeError> {
|
fn generate_test_runtime<'a>(mut args: Vec<&'static str>) -> Result<Runtime<'a>, RuntimeError> {
|
||||||
let mut cli_args = vec!["imag-link",
|
let mut cli_args = vec!["imag-link",
|
||||||
"--no-color",
|
"--no-color",
|
||||||
|
@ -417,7 +412,7 @@ version = \"0.3.0\"\
|
||||||
cli_args.append(&mut args);
|
cli_args.append(&mut args);
|
||||||
|
|
||||||
let cli_app = MockLinkApp::new(cli_args);
|
let cli_app = MockLinkApp::new(cli_args);
|
||||||
Runtime::new(cli_app)
|
Runtime::with_configuration(cli_app, generate_test_config())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_test_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> StoreResult<StoreId> {
|
fn create_test_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> StoreResult<StoreId> {
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
[store]
|
|
||||||
implicit-create = true
|
|
||||||
store-unload-hook-aspects = []
|
|
||||||
pre-create-hook-aspects = []
|
|
||||||
post-create-hook-aspects = []
|
|
||||||
pre-move-hook-aspects = []
|
|
||||||
post-move-hook-aspects = []
|
|
||||||
pre-retrieve-hook-aspects = []
|
|
||||||
post-retrieve-hook-aspects = []
|
|
||||||
pre-update-hook-aspects = []
|
|
||||||
post-update-hook-aspects = []
|
|
||||||
pre-delete-hook-aspects = []
|
|
||||||
post-delete-hook-aspects = []
|
|
||||||
|
|
||||||
[store.aspects.debug]
|
|
||||||
parallel = false
|
|
||||||
|
|
||||||
[store.aspects.vcs]
|
|
||||||
parallel = false
|
|
||||||
|
|
||||||
[store.hooks.stdhook_debug]
|
|
||||||
aspect = "vcs"
|
|
||||||
|
|
||||||
[store.hooks.stdhook_git_update]
|
|
||||||
aspect = "vcs"
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
[store.hooks.stdhook_git_update.commit]
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
[store.hooks.stdhook_git_delete]
|
|
||||||
aspect = "vcs"
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
[store.hooks.stdhook_git_delete.commit]
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
[store.hooks.stdhook_git_storeunload]
|
|
||||||
aspect = "vcs"
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
[store.hooks.stdhook_git_storeunload.commit]
|
|
||||||
enabled = false
|
|
|
@ -73,21 +73,33 @@ impl Configuration {
|
||||||
/// with all variants.
|
/// with all variants.
|
||||||
///
|
///
|
||||||
/// If that doesn't work either, an error is returned.
|
/// If that doesn't work either, an error is returned.
|
||||||
pub fn new(toml_config: Value) -> Configuration {
|
pub fn new(rtp: &PathBuf) -> Result<Configuration> {
|
||||||
let verbosity = get_verbosity(&toml_config);
|
fetch_config(&rtp).map(|cfg| {
|
||||||
let editor = get_editor(&toml_config);
|
let verbosity = get_verbosity(&cfg);
|
||||||
let editor_opts = get_editor_opts(&toml_config);
|
let editor = get_editor(&cfg);
|
||||||
|
let editor_opts = get_editor_opts(&cfg);
|
||||||
|
|
||||||
debug!("Building configuration");
|
debug!("Building configuration");
|
||||||
debug!(" - verbosity : {:?}", verbosity);
|
debug!(" - verbosity : {:?}", verbosity);
|
||||||
debug!(" - editor : {:?}", editor);
|
debug!(" - editor : {:?}", editor);
|
||||||
debug!(" - editor-opts: {}", editor_opts);
|
debug!(" - editor-opts: {}", editor_opts);
|
||||||
|
|
||||||
Configuration {
|
Configuration {
|
||||||
config: toml_config,
|
config: cfg,
|
||||||
verbosity: verbosity,
|
verbosity: verbosity,
|
||||||
editor: editor,
|
editor: editor,
|
||||||
editor_opts: editor_opts,
|
editor_opts: editor_opts,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a new configuration object built from the given toml value.
|
||||||
|
pub fn with_value(value: Value) -> Configuration {
|
||||||
|
Configuration{
|
||||||
|
verbosity: get_verbosity(&value),
|
||||||
|
editor: get_editor(&value),
|
||||||
|
editor_opts: get_editor_opts(&value),
|
||||||
|
config: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,73 +223,67 @@ fn get_editor_opts(v: &Value) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GetConfiguration {
|
/// Helper to fetch the config file
|
||||||
/// Helper to fetch the config file
|
///
|
||||||
///
|
/// Tests several variants for the config file path and uses the first one which works.
|
||||||
/// Default implementation tests several variants for the config file path and uses the first
|
fn fetch_config(rtp: &PathBuf) -> Result<Value> {
|
||||||
/// one which works.
|
use std::env;
|
||||||
fn get_configuration(&self, rtp: &PathBuf) -> Result<Configuration> {
|
use std::fs::File;
|
||||||
use std::env;
|
use std::io::Read;
|
||||||
use std::fs::File;
|
use std::io::Write;
|
||||||
use std::io::Read;
|
use std::io::stderr;
|
||||||
use std::io::Write;
|
|
||||||
use std::io::stderr;
|
|
||||||
|
|
||||||
use xdg_basedir;
|
use xdg_basedir;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use libimagutil::variants::generate_variants as gen_vars;
|
use libimagutil::variants::generate_variants as gen_vars;
|
||||||
use libimagerror::trace::trace_error;
|
use libimagerror::trace::trace_error;
|
||||||
|
|
||||||
let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
|
let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
|
||||||
let modifier = |base: &PathBuf, v: &'static str| {
|
let modifier = |base: &PathBuf, v: &'static str| {
|
||||||
let mut base = base.clone();
|
let mut base = base.clone();
|
||||||
base.push(String::from(v));
|
base.push(String::from(v));
|
||||||
base
|
base
|
||||||
};
|
};
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
vec![rtp.clone()],
|
vec![rtp.clone()],
|
||||||
gen_vars(rtp.clone(), variants.clone(), &modifier),
|
gen_vars(rtp.clone(), variants.clone(), &modifier),
|
||||||
|
|
||||||
env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
|
env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
|
||||||
.unwrap_or(vec![]),
|
.unwrap_or(vec![]),
|
||||||
|
|
||||||
xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
|
xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
|
||||||
.unwrap_or(vec![]),
|
.unwrap_or(vec![]),
|
||||||
].iter()
|
].iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter(|path| path.exists() && path.is_file())
|
.filter(|path| path.exists() && path.is_file())
|
||||||
.map(|path| {
|
.map(|path| {
|
||||||
let content = {
|
let content = {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
let f = File::open(path);
|
let f = File::open(path);
|
||||||
if f.is_err() {
|
if f.is_err() {
|
||||||
return None
|
return None
|
||||||
}
|
|
||||||
let mut f = f.unwrap();
|
|
||||||
f.read_to_string(&mut s).ok();
|
|
||||||
s
|
|
||||||
};
|
|
||||||
|
|
||||||
match ::toml::de::from_str(&content[..]) {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(e) => {
|
|
||||||
write!(stderr(), "Config file parser error:").ok();
|
|
||||||
trace_error(&e);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
let mut f = f.unwrap();
|
||||||
.filter(|loaded| loaded.is_some())
|
f.read_to_string(&mut s).ok();
|
||||||
.nth(0)
|
s
|
||||||
.map(|inner| Value::Table(inner.unwrap()))
|
};
|
||||||
.ok_or(ConfigErrorKind::NoConfigFileFound.into())
|
|
||||||
.map(Configuration::new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> GetConfiguration for App<'a, 'a> {}
|
match ::toml::de::from_str(&content[..]) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(e) => {
|
||||||
|
write!(stderr(), "Config file parser error:").ok();
|
||||||
|
trace_error(&e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(|loaded| loaded.is_some())
|
||||||
|
.nth(0)
|
||||||
|
.map(|inner| Value::Table(inner.unwrap()))
|
||||||
|
.ok_or(ConfigErrorKind::NoConfigFileFound.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub trait InternalConfiguration {
|
pub trait InternalConfiguration {
|
||||||
fn enable_logging(&self) -> bool {
|
fn enable_logging(&self) -> bool {
|
||||||
|
|
|
@ -29,7 +29,7 @@ use clap::{Arg, ArgMatches};
|
||||||
use log;
|
use log;
|
||||||
use log::LogLevelFilter;
|
use log::LogLevelFilter;
|
||||||
|
|
||||||
use configuration::{Configuration, GetConfiguration, InternalConfiguration};
|
use configuration::{Configuration, InternalConfiguration};
|
||||||
use error::RuntimeError;
|
use error::RuntimeError;
|
||||||
use error::RuntimeErrorKind;
|
use error::RuntimeErrorKind;
|
||||||
use error::MapErrInto;
|
use error::MapErrInto;
|
||||||
|
@ -56,14 +56,9 @@ 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>(mut cli_app: C) -> Result<Runtime<'a>, RuntimeError>
|
pub fn new<C>(cli_app: C) -> Result<Runtime<'a>, RuntimeError>
|
||||||
where C: Clone + CliSpec<'a> + GetConfiguration + InternalConfiguration
|
where C: Clone + CliSpec<'a> + InternalConfiguration
|
||||||
{
|
{
|
||||||
use std::env;
|
|
||||||
use std::io::stdout;
|
|
||||||
|
|
||||||
use clap::Shell;
|
|
||||||
|
|
||||||
use libimagerror::trace::trace_error;
|
use libimagerror::trace::trace_error;
|
||||||
use libimagerror::into::IntoError;
|
use libimagerror::into::IntoError;
|
||||||
|
|
||||||
|
@ -71,11 +66,51 @@ impl<'a> Runtime<'a> {
|
||||||
|
|
||||||
let matches = cli_app.clone().matches();
|
let matches = cli_app.clone().matches();
|
||||||
|
|
||||||
let is_debugging = matches.is_present("debugging");
|
let rtp = get_rtp_match(&matches);
|
||||||
let is_verbose = matches.is_present("verbosity");
|
|
||||||
let colored = !matches.is_present("no-color-output");
|
let configpath = matches.value_of(Runtime::arg_config_name())
|
||||||
|
.map_or_else(|| rtp.clone(), PathBuf::from);
|
||||||
|
|
||||||
|
let config = match Configuration::new(&configpath) {
|
||||||
|
Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
|
||||||
|
return Err(RuntimeErrorKind::Instantiate.into_error_with_cause(Box::new(e)));
|
||||||
|
} else {
|
||||||
|
warn!("No config file found.");
|
||||||
|
warn!("Continuing without configuration file");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
|
||||||
|
Ok(mut config) => {
|
||||||
|
if let Err(e) = config.override_config(get_override_specs(&matches)) {
|
||||||
|
error!("Could not apply config overrides");
|
||||||
|
trace_error(&e);
|
||||||
|
|
||||||
|
// TODO: continue question (interactive)
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(config)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Runtime::with_configuration(cli_app, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds the Runtime object using the given `config`.
|
||||||
|
pub fn with_configuration<C>(mut cli_app: C, config: Option<Configuration>) -> Result<Runtime<'a>, RuntimeError>
|
||||||
|
where C: Clone + CliSpec<'a> + InternalConfiguration
|
||||||
|
{
|
||||||
|
use std::io::stdout;
|
||||||
|
|
||||||
|
use clap::Shell;
|
||||||
|
|
||||||
|
let matches = cli_app.clone().matches();
|
||||||
|
|
||||||
|
let is_debugging = matches.is_present(Runtime::arg_debugging_name());
|
||||||
|
|
||||||
if cli_app.enable_logging() {
|
if cli_app.enable_logging() {
|
||||||
|
let is_verbose = matches.is_present(Runtime::arg_verbosity_name());
|
||||||
|
let colored = !matches.is_present(Runtime::arg_no_color_output_name());
|
||||||
|
|
||||||
Runtime::init_logger(is_debugging, is_verbose, colored);
|
Runtime::init_logger(is_debugging, is_verbose, colored);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,64 +124,29 @@ impl<'a> Runtime<'a> {
|
||||||
_ => debug!("Not generating shell completion script"),
|
_ => debug!("Not generating shell completion script"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let rtp : PathBuf = matches.value_of("runtimepath")
|
let rtp = get_rtp_match(&matches);
|
||||||
.map_or_else(|| {
|
|
||||||
env::var("HOME")
|
let storepath = matches.value_of(Runtime::arg_storepath_name())
|
||||||
.map(PathBuf::from)
|
|
||||||
.map(|mut p| { p.push(".imag"); p})
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
|
|
||||||
})
|
|
||||||
}, PathBuf::from);
|
|
||||||
let storepath = matches.value_of("storepath")
|
|
||||||
.map_or_else(|| {
|
.map_or_else(|| {
|
||||||
let mut spath = rtp.clone();
|
let mut spath = rtp.clone();
|
||||||
spath.push("store");
|
spath.push("store");
|
||||||
spath
|
spath
|
||||||
}, PathBuf::from);
|
}, PathBuf::from);
|
||||||
|
|
||||||
let configpath = matches.value_of("config")
|
let store_config = match config {
|
||||||
.map_or_else(|| rtp.clone(), PathBuf::from);
|
|
||||||
|
|
||||||
debug!("RTP path = {:?}", rtp);
|
|
||||||
debug!("Store path = {:?}", storepath);
|
|
||||||
debug!("Config path = {:?}", configpath);
|
|
||||||
|
|
||||||
let cfg = match cli_app.get_configuration(&configpath) {
|
|
||||||
Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
|
|
||||||
return Err(RuntimeErrorKind::Instantiate.into_error_with_cause(Box::new(e)));
|
|
||||||
} else {
|
|
||||||
warn!("No config file found.");
|
|
||||||
warn!("Continuing without configuration file");
|
|
||||||
None
|
|
||||||
},
|
|
||||||
|
|
||||||
Ok(mut cfg) => {
|
|
||||||
if let Err(e) = cfg.override_config(get_override_specs(&matches)) {
|
|
||||||
error!("Could not apply config overrides");
|
|
||||||
trace_error(&e);
|
|
||||||
|
|
||||||
// TODO: continue question (interactive)
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(cfg)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let store_config = match cfg {
|
|
||||||
Some(ref c) => c.store_config().cloned(),
|
Some(ref c) => c.store_config().cloned(),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_debugging {
|
if is_debugging {
|
||||||
write!(stderr(), "Config: {:?}\n", cfg).ok();
|
write!(stderr(), "Config: {:?}\n", config).ok();
|
||||||
write!(stderr(), "Store-config: {:?}\n", store_config).ok();
|
write!(stderr(), "Store-config: {:?}\n", store_config).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
Store::new(storepath.clone(), store_config).map(|store| {
|
Store::new(storepath, store_config).map(|store| {
|
||||||
Runtime {
|
Runtime {
|
||||||
cli_matches: matches,
|
cli_matches: matches,
|
||||||
configuration: cfg,
|
configuration: config,
|
||||||
rtp: rtp,
|
rtp: rtp,
|
||||||
store: store,
|
store: store,
|
||||||
}
|
}
|
||||||
|
@ -408,6 +408,22 @@ impl<'a> Runtime<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_rtp_match<'a>(matches: &ArgMatches<'a>) -> PathBuf {
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
matches.value_of(Runtime::arg_runtimepath_name())
|
||||||
|
.map_or_else(|| {
|
||||||
|
env::var("HOME")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.map(|mut p| { p.push(".imag"); p })
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!("You seem to be $HOME-less. Please get a $HOME before using this \
|
||||||
|
software. We are sorry for you and hope you have some \
|
||||||
|
accommodation anyways.");
|
||||||
|
})
|
||||||
|
}, PathBuf::from)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_override_specs(matches: &ArgMatches) -> Vec<String> {
|
fn get_override_specs(matches: &ArgMatches) -> Vec<String> {
|
||||||
matches
|
matches
|
||||||
.values_of("config-override")
|
.values_of("config-override")
|
||||||
|
|
Loading…
Reference in a new issue