mithril_client_cli/
configuration.rs1use serde::Deserialize;
2use std::collections::HashMap;
3use thiserror::Error;
4
5use mithril_client::MithrilError;
6
7#[derive(Debug, Error)]
9pub enum ConfigError {
10 #[error("Parameter '{0}' is mandatory.")]
12 Required(String),
13
14 #[error("Parameter '{0}' cannot be converted to string")]
16 Conversion(String, #[source] MithrilError),
17}
18
19#[derive(Debug, Default, PartialEq, Deserialize)]
21#[serde(default)]
22pub struct ConfigParameters {
23 parameters: HashMap<String, String>,
24}
25
26impl ConfigParameters {
27 pub fn new(parameters: HashMap<String, String>) -> Self {
29 Self { parameters }
30 }
31
32 #[cfg(test)]
34 pub fn build(parameters: &[(&str, &str)]) -> Self {
35 let parameters = parameters
36 .iter()
37 .map(|(k, v)| (k.to_string(), v.to_string()))
38 .collect();
39
40 Self::new(parameters)
41 }
42
43 #[cfg(test)]
45 pub fn add_parameter(&mut self, name: &str, value: &str) -> &mut Self {
46 let _ = self.parameters.insert(name.to_string(), value.to_string());
47
48 self
49 }
50
51 pub fn add_source(&mut self, source: &impl ConfigSource) -> Result<(), ConfigError> {
53 let extra = source.collect()?;
54 self.parameters.extend(extra);
55
56 Ok(())
57 }
58
59 pub fn get(&self, name: &str) -> Option<String> {
61 self.parameters.get(name).cloned()
62 }
63
64 pub fn get_or(&self, name: &str, default: &str) -> String {
67 self.get(name).unwrap_or(default.to_string())
68 }
69
70 pub fn require(&self, name: &str) -> Result<String, ConfigError> {
73 self.get(name).ok_or_else(|| ConfigError::Required(name.to_string()))
74 }
75}
76
77pub trait ConfigSource {
79 fn collect(&self) -> Result<HashMap<String, String>, ConfigError>;
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 struct TestSource {
88 params: HashMap<String, String>,
89 }
90
91 impl<const N: usize> From<[(&str, &str); N]> for TestSource {
92 fn from(arr: [(&str, &str); N]) -> Self {
93 TestSource {
94 params: arr.into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(),
95 }
96 }
97 }
98
99 impl ConfigSource for TestSource {
100 fn collect(&self) -> Result<HashMap<String, String>, ConfigError> {
101 Ok(self.params.clone())
102 }
103 }
104
105 #[test]
106 fn test_config_constructor() {
107 let config = ConfigParameters::build(&[("pika", "chu")]);
108
109 assert_eq!(
110 ConfigParameters {
111 parameters: [("pika".to_string(), "chu".to_string())].into_iter().collect()
112 },
113 config
114 );
115 }
116 #[test]
117 fn test_config_set() {
118 let mut config = ConfigParameters::default();
119 config.add_parameter("pika", "chu");
120
121 assert_eq!(
122 ConfigParameters {
123 parameters: [("pika".to_string(), "chu".to_string())].into_iter().collect()
124 },
125 config
126 );
127 }
128
129 #[test]
130 fn test_config_get() {
131 let mut config = ConfigParameters::default();
132 config.add_parameter("pika", "chu");
133
134 assert_eq!("chu".to_string(), config.get("pika").unwrap());
135 assert!(config.get("whatever").is_none());
136 }
137
138 #[test]
139 fn test_config_default() {
140 let mut config = ConfigParameters::default();
141 config.add_parameter("pika", "chu");
142
143 assert_eq!("chu".to_string(), config.get("pika").unwrap());
144 assert_eq!("default".to_string(), config.get_or("whatever", "default"));
145 }
146
147 #[test]
148 fn test_config_require() {
149 let mut config = ConfigParameters::default();
150 config.add_parameter("pika", "chu");
151
152 assert_eq!("chu".to_string(), config.require("pika").unwrap());
153 config.require("whatever").unwrap_err();
154 }
155
156 #[test]
157 fn test_add_source_to_config() {
158 let mut config = ConfigParameters::build(&[("pika", "chu"), ("chari", "zard")]);
159 config.add_source(&TestSource::from([("jiggly", "puff")])).unwrap();
160
161 assert_eq!(
162 ConfigParameters {
163 parameters: HashMap::from([
164 ("pika".to_string(), "chu".to_string()),
165 ("chari".to_string(), "zard".to_string()),
166 ("jiggly".to_string(), "puff".to_string())
167 ])
168 },
169 config
170 );
171 }
172
173 #[test]
174 fn test_add_source_replace_existing_value() {
175 let mut config = ConfigParameters::build(&[("pika", "pika")]);
176 config.add_source(&TestSource::from([("pika", "not chu")])).unwrap();
177
178 assert_eq!(
179 ConfigParameters {
180 parameters: HashMap::from([("pika".to_string(), "not chu".to_string()),])
181 },
182 config
183 );
184 }
185}