use super::{config,*};
use std::fs;
use regex::Regex;
use crate::LoggingOptions;
pub fn merge_configs(mut low_priority: Config, mut high_priority: Config) -> Config {
if let LoggingOptions::ToFile { file_path: ref _file_path, rotation_size: mut _rotation_size, rotations_kept: mut _rotations_kept, compress_rotated: mut _compress_rotated } = high_priority.log {
if _rotation_size == 0 {
if let LoggingOptions::ToFile { file_path: _l_file_path, rotation_size: l_rotation_size, rotations_kept: l_rotations_kept, compress_rotated: l_compress_rotated } = low_priority.log {
_rotation_size = l_rotation_size;
_rotations_kept = l_rotations_kept;
_compress_rotated = l_compress_rotated;
} else {
_rotation_size = 1024*1024*1024;
_rotations_kept = 64;
_compress_rotated = true;
}
}
}
if !high_priority.services.is_enabled() {
high_priority.services = ExtendedOption::Enabled(ServicesConfig {
web: ExtendedOption::Unset,
socket_server: ExtendedOption::Unset,
telegram: ExtendedOption::Unset
});
}
if !low_priority.services.is_enabled() {
low_priority.services = ExtendedOption::Enabled(ServicesConfig {
web: ExtendedOption::Unset,
socket_server: ExtendedOption::Unset,
telegram: ExtendedOption::Unset
});
}
if let ExtendedOption::Enabled(l_telegram) = &low_priority.services.telegram {
high_priority.services.telegram = ExtendedOption::Enabled(l_telegram.clone());
}
if let ExtendedOption::Enabled(l_web) = &low_priority.services.web {
high_priority.services.web = ExtendedOption::Enabled(l_web.clone());
}
if let ExtendedOption::Enabled(l_socket_server) = &low_priority.services.socket_server {
high_priority.services.socket_server = ExtendedOption::Enabled(l_socket_server.clone());
}
high_priority.tokio_threads = if high_priority.tokio_threads > 0 {
high_priority.tokio_threads
} else if low_priority.tokio_threads > 0 {
low_priority.tokio_threads
} else {
0
};
high_priority
}
pub fn load_or_create_default(config_file_path: &str) -> Result<Config, Box<dyn std::error::Error>> {
let result = load_from_file(config_file_path);
if result.is_ok() {
result
} else {
let error_message = result.as_ref().unwrap_err().to_string();
let default_config = Config::default();
if error_message.contains("No such file or directory") {
save_to_file(&default_config, config_file_path)?;
Ok(default_config)
} else {
result
}
}
}
fn load_from_file(config_file_path: &str) -> Result<Config, Box<dyn std::error::Error>> {
let ron_file_contents = fs::read_to_string(config_file_path)?;
let ron_options = ron::Options::default()
.with_default_extension(ron_extensions());
ron_options.from_str(&ron_file_contents)
.map_err(|err| Box::from(format!("config_ops.rs: Error deserializing contents of file '{}' as RON: {} -- HINT: delete the config file and let it be regenerated with all the default options", config_file_path, err)))
}
const CONFIG_MODELS_DOCS: &str = include_str!("config.rs");
fn save_to_file(config: &Config, config_file_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let data_section = ron::ser::to_string_pretty(
&config,
ron::ser::PrettyConfig::new()
.depth_limit(10)
.new_line(String::from("\n"))
.indentor(String::from(" "))
.separate_tuple_members(true)
.enumerate_arrays(true)
.extensions(ron_extensions()))
.map_err(|err| format!("config.rs: Error serializing config as TOML: {}", err))?;
let docs_section = config::REPLACEMENTS.iter()
.fold(String::from(CONFIG_MODELS_DOCS), |s, (from, to)| {
let regex = Regex::new(from).expect("Error parsing regex");
regex.replace_all(&s, *to).to_string()
});
let config_file_contents = format!("{}\n\n/*{}*/\n", data_section, docs_section);
fs::write(config_file_path, config_file_contents)
.map_err(|err| Box::from(format!("config_ops.rs: Error writing default RON config to file '{}': {}", config_file_path, err)))
}
fn ron_extensions() -> ron::extensions::Extensions {
let mut extensions = ron::extensions::Extensions::empty();
extensions.insert(ron::extensions::Extensions::IMPLICIT_SOME);
extensions.insert(ron::extensions::Extensions::UNWRAP_NEWTYPES);
extensions
}
#[cfg(any(test,doc))]
mod tests {
use super::*;
const TEST_CONFIG_FILE: &str = "/tmp/kickass-app-template-tests.config.ron";
#[cfg_attr(not(doc),test)]
fn file_load_and_save() {
fs::remove_file(TEST_CONFIG_FILE).unwrap_or(());
let result = load_from_file(TEST_CONFIG_FILE);
println!("Loading from inexisting file: gives '{:?}'", result);
assert!(result.is_err(), "Loading from an non existing file should have returned an error");
let error_message = result.unwrap_err().to_string();
assert!(error_message.contains("No such file or directory"), "Error message '{}' does not contain 'No such file or directory'", error_message);
save_to_file(&Config::default(), TEST_CONFIG_FILE)
.expect("Could not save config file");
let _result = load_from_file(TEST_CONFIG_FILE)
.expect("Could not load config file just created by save. Does RON have a bug or its default saving & loading parameters needs tweaking?");
let _result = load_or_create_default(TEST_CONFIG_FILE)
.expect("Could not load_or_create_default() for an existing file");
fs::remove_file(TEST_CONFIG_FILE).unwrap_or(());
let _result = load_or_create_default(TEST_CONFIG_FILE)
.expect("Could not load_or_create_default() for a non existing file");
}
#[cfg_attr(not(doc),test)]
fn merging_completenes() {
let low = Config {
log: LoggingOptions::Quiet,
services: ExtendedOption::Unset,
tokio_threads: 0,
ui: ExtendedOption::Unset,
};
let high = Config::default();
let expected = Config::default();
let merged = merge_configs(low, high);
assert_eq!(merged, expected, "'merge_configs() seem to not be covering newly added configs well: High priority config got (wrongly?) overridden by low priority");
let low = Config::default();
let high = Config {
log: LoggingOptions::ToConsole,
services: ExtendedOption::Unset,
tokio_threads: 0,
ui: ExtendedOption::Unset,
};
let expected = Config::default();
let merged = merge_configs(low, high);
assert_eq!(merged, expected, "'merge_configs() seem to not be covering newly added configs well: Low priority config wasn't able to set unset properties in the high priority");
}
}