1use crate::{Error, ErrorKind};
2use serde::Deserialize;
3use std::collections::HashMap;
4use std::env;
5use std::fs;
6
7#[derive(Deserialize, Clone)]
8pub struct Config {
9 pub parent: Option<String>,
10 pub threads_count: Option<usize>,
11 pub ignore_panic: Option<bool>,
12 pub repeat_count: Option<usize>,
13 pub pipe: Option<String>,
14 pub program: Option<String>,
15 pub current_dir: Option<String>,
16 pub args_template: Option<String>,
17 pub input_list: Option<Vec<String>>,
18 pub input_dir: Option<String>,
19 pub input_absolute: Option<bool>,
20 pub input_recursive: Option<bool>,
21 pub output_dir: Option<String>,
22 pub output_absolute: Option<bool>,
23 pub output_recursive: Option<bool>,
24 pub output_overwrite: Option<String>,
25 pub output_extension: Option<String>,
26 pub output_prefix: Option<String>,
27 pub output_suffix: Option<String>,
28 pub output_suffix_serial: Option<bool>,
29}
30
31impl Default for Config {
32 fn default() -> Self {
33 Self {
34 parent: None,
35 threads_count: Some(num_cpus::get()),
36 ignore_panic: Some(false),
37 repeat_count: Some(1),
38 pipe: None, program: None,
40 current_dir: None, args_template: Some(String::new()),
42 input_list: None,
43 input_dir: None,
44 input_absolute: Some(false),
45 input_recursive: Some(false),
46 output_dir: None,
47 output_absolute: Some(false),
48 output_recursive: Some(false),
49 output_overwrite: Some("allow".to_string()), output_extension: None,
51 output_prefix: None,
52 output_suffix: None,
53 output_suffix_serial: Some(false),
54 }
55 }
56}
57
58impl Config {
59 pub fn merge(&mut self, parent: &Self) {
60 macro_rules! m {
61 ( $( $key:ident ),* ) => {
62 $(
63 if self.$key.is_none() && parent.$key.is_some() {
64 self.$key = parent.$key.clone();
65 }
66 )*
67 };
68 }
69 m!(
71 parent,
72 threads_count,
73 ignore_panic,
74 repeat_count,
75 pipe,
76 program,
77 current_dir,
78 args_template,
79 input_list,
80 input_dir,
81 input_absolute,
82 input_recursive,
83 output_dir,
84 output_absolute,
85 output_recursive,
86 output_overwrite,
87 output_extension,
88 output_prefix,
89 output_suffix,
90 output_suffix_serial
91 );
92 }
93}
94
95#[derive(Deserialize)]
96pub struct Profile {
97 presets: HashMap<String, Config>,
98 pub current: Option<String>,
99 pub export: Option<Vec<String>>,
100 pub log_level: Option<i32>,
101 pub interactive: Option<bool>,
102}
103
104impl Profile {
105 fn fit(mut self) -> Self {
106 self.export = self.export.or(Some(Vec::new()));
107 self.log_level = self.log_level.or(Some(2));
108 self.interactive = self.interactive.or(Some(true));
109 self
110 }
111
112 fn get(&self, name: &str) -> Result<Config, Error> {
113 let mut current = self.presets.get(name).unwrap().clone();
114
115 let mut depth = 0;
117 while let Some(parent_name) = current.parent.take() {
118 depth += 1;
119 if depth > 64 {
120 return Err(Error {
121 kind: ErrorKind::Config,
122 message: "preset inherit depth > 64".to_string(),
123 ..Default::default()
124 });
125 }
126 if let Some(parent) = self.presets.get(&parent_name) {
127 current.merge(parent);
128 current.parent = parent.parent.clone();
129 } else {
130 return Err(Error {
131 kind: ErrorKind::Config,
132 message: format!("parent preset `{parent_name}` not found"),
133 ..Default::default()
134 });
135 }
136 }
137
138 if let Some(parent) = self.presets.get("global") {
140 current.merge(parent);
141 }
142 current.merge(&Default::default());
143
144 Ok(current)
145 }
146
147 pub fn get_current(&self) -> Result<Config, Error> {
148 self.get(self.current.as_ref().unwrap())
149 }
150
151 pub fn keys(&self) -> Vec<&String> {
152 let list = self.export.as_ref().unwrap();
153 self.presets.keys().filter(|k| list.contains(k)).collect()
154 }
155
156 pub fn from_toml(toml_str: &str) -> Result<Self, Error> {
157 Ok(Self::fit(toml::from_str(toml_str).map_err(|e| Error {
158 kind: ErrorKind::Config,
159 inner: Box::new(e),
160 message: "error while config file deserialize".to_string(),
161 })?))
162 }
163
164 pub fn from_default_file() -> Result<Self, Error> {
165 let path = env::current_exe().unwrap();
166 if let Ok(text) = fs::read_to_string(&path.with_extension("toml")) {
167 return Self::from_toml(&text);
168 }
169 Err(Error {
170 kind: ErrorKind::Config,
171 message: "the config file was not found".to_string(),
172 ..Default::default()
173 })
174 }
175}