mithril_client_cli/
configuration.rs
1use 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<Self, ConfigError> {
53 let extra = source.collect()?;
54 self.parameters.extend(extra);
55
56 Ok(self)
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)
74 .ok_or_else(|| ConfigError::Required(name.to_string()))
75 }
76}
77
78pub trait ConfigSource {
80 fn collect(&self) -> Result<HashMap<String, String>, ConfigError>;
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 struct TestSource {
89 params: HashMap<String, String>,
90 }
91
92 impl<const N: usize> From<[(&str, &str); N]> for TestSource {
93 fn from(arr: [(&str, &str); N]) -> Self {
94 TestSource {
95 params: arr
96 .into_iter()
97 .map(|(k, v)| (k.to_string(), v.to_string()))
98 .collect(),
99 }
100 }
101 }
102
103 impl ConfigSource for TestSource {
104 fn collect(&self) -> Result<HashMap<String, String>, ConfigError> {
105 Ok(self.params.clone())
106 }
107 }
108
109 #[test]
110 fn test_config_constructor() {
111 let config = ConfigParameters::build(&[("pika", "chu")]);
112
113 assert_eq!(
114 ConfigParameters {
115 parameters: [("pika".to_string(), "chu".to_string())]
116 .into_iter()
117 .collect()
118 },
119 config
120 );
121 }
122 #[test]
123 fn test_config_set() {
124 let mut config = ConfigParameters::default();
125 config.add_parameter("pika", "chu");
126
127 assert_eq!(
128 ConfigParameters {
129 parameters: [("pika".to_string(), "chu".to_string())]
130 .into_iter()
131 .collect()
132 },
133 config
134 );
135 }
136
137 #[test]
138 fn test_config_get() {
139 let mut config = ConfigParameters::default();
140 config.add_parameter("pika", "chu");
141
142 assert_eq!("chu".to_string(), config.get("pika").unwrap());
143 assert!(config.get("whatever").is_none());
144 }
145
146 #[test]
147 fn test_config_default() {
148 let mut config = ConfigParameters::default();
149 config.add_parameter("pika", "chu");
150
151 assert_eq!("chu".to_string(), config.get("pika").unwrap());
152 assert_eq!("default".to_string(), config.get_or("whatever", "default"));
153 }
154
155 #[test]
156 fn test_config_require() {
157 let mut config = ConfigParameters::default();
158 config.add_parameter("pika", "chu");
159
160 assert_eq!("chu".to_string(), config.require("pika").unwrap());
161 config.require("whatever").unwrap_err();
162 }
163
164 #[test]
165 fn test_add_source_to_config() {
166 let config = ConfigParameters::build(&[("pika", "chu"), ("chari", "zard")])
167 .add_source(&TestSource::from([("jiggly", "puff")]))
168 .unwrap();
169
170 assert_eq!(
171 ConfigParameters {
172 parameters: HashMap::from([
173 ("pika".to_string(), "chu".to_string()),
174 ("chari".to_string(), "zard".to_string()),
175 ("jiggly".to_string(), "puff".to_string())
176 ])
177 },
178 config
179 );
180 }
181
182 #[test]
183 fn test_add_source_replace_existing_value() {
184 let config = ConfigParameters::build(&[("pika", "pika")])
185 .add_source(&TestSource::from([("pika", "not chu")]))
186 .unwrap();
187
188 assert_eq!(
189 ConfigParameters {
190 parameters: HashMap::from([("pika".to_string(), "not chu".to_string()),])
191 },
192 config
193 );
194 }
195}