diff options
-rw-r--r-- | imag-link/src/main.rs | 21 | ||||
-rw-r--r-- | imag-link/test_config.toml | 43 | ||||
-rw-r--r-- | libimagrt/src/configuration.rs | 166 | ||||
-rw-r--r-- | libimagrt/src/runtime.rs | 122 |
4 files changed, 163 insertions, 189 deletions
diff --git a/imag-link/src/main.rs b/imag-link/src/main.rs index f254488b..18354bb0 100644 --- a/imag-link/src/main.rs +++ b/imag-link/src/main.rs @@ -350,14 +350,11 @@ mod tests { use libimagrt::spec::CliSpec; use libimagrt::runtime::Runtime; use libimagrt::error::RuntimeError; - use libimagrt::configuration::{Configuration, GetConfiguration, InternalConfiguration, - Result as ConfigResult, ConfigErrorKind}; - use libimagrt::configuration::error::MapErrInto; + use libimagrt::configuration::{Configuration, InternalConfiguration}; use libimagstore::storeid::StoreId; use libimagstore::store::{Result as StoreResult, FileLockEntry}; - static TOML_CONFIG_FILE: &[u8] = include_bytes!("../test_config.toml"); static DEFAULT_ENTRY: &str = "\ ---\ [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> { fn enable_logging(&self) -> bool { 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> { let mut cli_args = vec!["imag-link", "--no-color", @@ -417,7 +412,7 @@ version = \"0.3.0\"\ cli_args.append(&mut 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> { diff --git a/imag-link/test_config.toml b/imag-link/test_config.toml deleted file mode 100644 index 3041de6f..00000000 --- a/imag-link/test_config.toml +++ /dev/null @@ -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 diff --git a/libimagrt/src/configuration.rs b/libimagrt/src/configuration.rs index 78902c41..15e54356 100644 --- a/libimagrt/src/configuration.rs +++ b/libimagrt/src/configuration.rs @@ -73,21 +73,33 @@ impl Configuration { /// with all variants. /// /// If that doesn't work either, an error is returned. - pub fn new(toml_config: Value) -> Configuration { - let verbosity = get_verbosity(&toml_config); - let editor = get_editor(&toml_config); - let editor_opts = get_editor_opts(&toml_config); - - debug!("Building configuration"); - debug!(" - verbosity : {:?}", verbosity); - debug!(" - editor : {:?}", editor); - debug!(" - editor-opts: {}", editor_opts); - - Configuration { - config: toml_config, - verbosity: verbosity, - editor: editor, - editor_opts: editor_opts, + pub fn new(rtp: &PathBuf) -> Result<Configuration> { + fetch_config(&rtp).map(|cfg| { + let verbosity = get_verbosity(&cfg); + let editor = get_editor(&cfg); + let editor_opts = get_editor_opts(&cfg); + + debug!("Building configuration"); + debug!(" - verbosity : {:?}", verbosity); + debug!(" - editor : {:?}", editor); + debug!(" - editor-opts: {}", editor_opts); + + Configuration { + config: cfg, + verbosity: verbosity, + editor: editor, + 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,74 +223,68 @@ fn get_editor_opts(v: &Value) -> String { } } -pub trait GetConfiguration { - /// Helper to fetch the config file - /// - /// Default implementation tests several variants for the config file path and uses the first - /// one which works. - fn get_configuration(&self, rtp: &PathBuf) -> Result<Configuration> { - use std::env; - use std::fs::File; - use std::io::Read; - use std::io::Write; - use std::io::stderr; - - use xdg_basedir; - use itertools::Itertools; - - use libimagutil::variants::generate_variants as gen_vars; - use libimagerror::trace::trace_error; - - let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"]; - let modifier = |base: &PathBuf, v: &'static str| { - let mut base = base.clone(); - base.push(String::from(v)); - base - }; - - vec![ - vec![rtp.clone()], - gen_vars(rtp.clone(), variants.clone(), &modifier), - - env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier)) - .unwrap_or(vec![]), - - xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier)) - .unwrap_or(vec![]), - ].iter() - .flatten() - .filter(|path| path.exists() && path.is_file()) - .map(|path| { - let content = { - let mut s = String::new(); - let f = File::open(path); - if f.is_err() { - 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 - } +/// Helper to fetch the config file +/// +/// Tests several variants for the config file path and uses the first one which works. +fn fetch_config(rtp: &PathBuf) -> Result<Value> { + use std::env; + use std::fs::File; + use std::io::Read; + use std::io::Write; + use std::io::stderr; + + use xdg_basedir; + use itertools::Itertools; + + use libimagutil::variants::generate_variants as gen_vars; + use libimagerror::trace::trace_error; + + let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"]; + let modifier = |base: &PathBuf, v: &'static str| { + let mut base = base.clone(); + base.push(String::from(v)); + base + }; + + vec![ + vec![rtp.clone()], + gen_vars(rtp.clone(), variants.clone(), &modifier), + + env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier)) + .unwrap_or(vec![]), + + xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier)) + .unwrap_or(vec![]), + ].iter() + .flatten() + .filter(|path| path.exists() && path.is_file()) + .map(|path| { + let content = { + let mut s = String::new(); + let f = File::open(path); + if f.is_err() { + return None } - }) - .filter(|loaded| loaded.is_some()) - .nth(0) - .map(|inner| Value::Table(inner.unwrap())) - .ok_or(ConfigErrorKind::NoConfigFileFound.into()) - .map(Configuration::new) - } + 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 + } + } + }) + .filter(|loaded| loaded.is_some()) + .nth(0) + .map(|inner| Value::Table(inner.unwrap())) + .ok_or(ConfigErrorKind::NoConfigFileFound.into()) } -impl<'a> GetConfiguration for App<'a, 'a> {} - pub trait InternalConfiguration { fn enable_logging(&self) -> bool { true diff --git a/libimagrt/src/runtime.rs b/libimagrt/src/runtime.rs index 57f4f170..0ec1de19 100644 --- a/libimagrt/src/runtime.rs +++ b/libimagrt/src/runtime.rs @@ -29,7 +29,7 @@ use clap::{Arg, ArgMatches}; use log; use log::LogLevelFilter; -use configuration::{Configuration, GetConfiguration, InternalConfiguration}; +use configuration::{Configuration, InternalConfiguration}; use error::RuntimeError; use error::RuntimeErrorKind; use error::MapErrInto; @@ -56,14 +56,9 @@ impl<'a> Runtime<'a> { /// and builds the Runtime object with it. /// /// 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> - where C: Clone + CliSpec<'a> + GetConfiguration + InternalConfiguration + pub fn new<C>(cli_app: C) -> Result<Runtime<'a>, RuntimeError> + where C: Clone + CliSpec<'a> + InternalConfiguration { - use std::env; - use std::io::stdout; - - use clap::Shell; - use libimagerror::trace::trace_error; use libimagerror::into::IntoError; @@ -71,11 +66,51 @@ impl<'a> Runtime<'a> { let matches = cli_app.clone().matches(); - let is_debugging = matches.is_present("debugging"); - let is_verbose = matches.is_present("verbosity"); - let colored = !matches.is_present("no-color-output"); + let rtp = get_rtp_match(&matches); + + 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() { + 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); } @@ -89,64 +124,29 @@ impl<'a> Runtime<'a> { _ => debug!("Not generating shell completion script"), } - let rtp : PathBuf = matches.value_of("runtimepath") - .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); - let storepath = matches.value_of("storepath") + let rtp = get_rtp_match(&matches); + + let storepath = matches.value_of(Runtime::arg_storepath_name()) .map_or_else(|| { let mut spath = rtp.clone(); spath.push("store"); spath }, PathBuf::from); - let configpath = matches.value_of("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 { + let store_config = match config { Some(ref c) => c.store_config().cloned(), None => None, }; if is_debugging { - write!(stderr(), "Config: {:?}\n", cfg).ok(); + write!(stderr(), "Config: {:?}\n", 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 { cli_matches: matches, - configuration: cfg, + configuration: config, rtp: rtp, 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> { matches .values_of("config-override") |