use crate::{Error, ErrorKind};
use serde::Deserialize;
use std::collections::HashMap;
use std::env;
use std::fs;
#[derive(Deserialize, Clone)]
pub struct Config {
pub parent: Option<String>,
pub threads_count: Option<usize>,
pub ignore_panic: Option<bool>,
pub repeat_count: Option<usize>,
pub pipe: Option<String>,
pub program: Option<String>,
pub current_dir: Option<String>,
pub args_template: Option<String>,
pub input_list: Option<Vec<String>>,
pub input_dir: Option<String>,
pub input_absolute: Option<bool>,
pub input_recursive: Option<bool>,
pub output_dir: Option<String>,
pub output_absolute: Option<bool>,
pub output_recursive: Option<bool>,
pub output_overwrite: Option<String>,
pub output_extension: Option<String>,
pub output_prefix: Option<String>,
pub output_suffix: Option<String>,
pub output_suffix_serial: Option<bool>,
}
impl Default for Config {
fn default() -> Self {
Self {
parent: None,
threads_count: Some(num_cpus::get()),
ignore_panic: Some(false),
repeat_count: Some(1),
pipe: None, program: None,
current_dir: None, args_template: Some(String::new()),
input_list: None,
input_dir: None,
input_absolute: Some(false),
input_recursive: Some(false),
output_dir: None,
output_absolute: Some(false),
output_recursive: Some(false),
output_overwrite: Some("allow".to_string()), output_extension: None,
output_prefix: None,
output_suffix: None,
output_suffix_serial: Some(false),
}
}
}
impl Config {
pub fn merge(&mut self, parent: &Self) {
macro_rules! m {
( $( $key:ident ),* ) => {
$(
if self.$key.is_none() && parent.$key.is_some() {
self.$key = parent.$key.clone();
}
)*
};
}
m!(
parent,
threads_count,
ignore_panic,
repeat_count,
pipe,
program,
current_dir,
args_template,
input_list,
input_dir,
input_absolute,
input_recursive,
output_dir,
output_absolute,
output_recursive,
output_overwrite,
output_extension,
output_prefix,
output_suffix,
output_suffix_serial
);
}
}
#[derive(Deserialize)]
pub struct Profile {
presets: HashMap<String, Config>,
pub current: Option<String>,
pub export: Option<Vec<String>>,
pub log_level: Option<i32>,
pub interactive: Option<bool>,
}
impl Profile {
fn fit(mut self) -> Self {
self.export = self.export.or(Some(Vec::new()));
self.log_level = self.log_level.or(Some(2));
self.interactive = self.interactive.or(Some(true));
self
}
fn get(&self, name: &str) -> Result<Config, Error> {
let mut current = self.presets.get(name).unwrap().clone();
let mut depth = 0;
while let Some(parent_name) = current.parent.take() {
depth += 1;
if depth > 64 {
return Err(Error {
kind: ErrorKind::Config,
message: "preset inherit depth > 64".to_string(),
..Default::default()
});
}
if let Some(parent) = self.presets.get(&parent_name) {
current.merge(parent);
current.parent = parent.parent.clone();
} else {
return Err(Error {
kind: ErrorKind::Config,
message: format!("parent preset `{parent_name}` not found"),
..Default::default()
});
}
}
if let Some(parent) = self.presets.get("global") {
current.merge(parent);
}
current.merge(&Default::default());
Ok(current)
}
pub fn get_current(&self) -> Result<Config, Error> {
self.get(self.current.as_ref().unwrap())
}
pub fn keys(&self) -> Vec<&String> {
let list = self.export.as_ref().unwrap();
self.presets.keys().filter(|k| list.contains(k)).collect()
}
pub fn from_toml(toml_str: &str) -> Result<Self, Error> {
Ok(Self::fit(toml::from_str(toml_str).map_err(|e| Error {
kind: ErrorKind::Config,
inner: Box::new(e),
message: "error while config file deserialize".to_string(),
})?))
}
pub fn from_default_file() -> Result<Self, Error> {
let path = env::current_exe().unwrap();
if let Ok(text) = fs::read_to_string(&path.with_extension("toml")) {
return Self::from_toml(&text);
}
Err(Error {
kind: ErrorKind::Config,
message: "the config file was not found".to_string(),
..Default::default()
})
}
}