1use std::ops::{Deref, DerefMut};
22
23use indexmap::IndexMap;
24use serde::{Deserialize, Serialize};
25use serde_json::Value;
26
27#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
35#[serde(transparent)]
36pub struct Params(IndexMap<String, Value>);
37
38impl Params {
39 pub fn new() -> Self {
41 Self(IndexMap::new())
42 }
43
44 pub fn from_index_map(map: IndexMap<String, Value>) -> Self {
46 Self(map)
47 }
48
49 pub fn get_u64(&self, key: &str) -> Option<u64> {
53 self.get(key).and_then(|v| v.as_u64())
54 }
55
56 pub fn get_i64(&self, key: &str) -> Option<i64> {
60 self.get(key).and_then(|v| v.as_i64())
61 }
62
63 pub fn get_usize(&self, key: &str) -> Option<usize> {
67 self.get(key).and_then(|v| v.as_u64()).map(|n| n as usize)
68 }
69
70 pub fn get_str(&self, key: &str) -> Option<&str> {
74 self.get(key).and_then(|v| v.as_str())
75 }
76
77 pub fn get_bool(&self, key: &str) -> Option<bool> {
81 self.get(key).and_then(|v| v.as_bool())
82 }
83
84 pub fn get_f64(&self, key: &str) -> Option<f64> {
88 self.get(key).and_then(|v| v.as_f64())
89 }
90
91 #[cfg(feature = "python")]
92 pub fn to_pydict(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<pyo3::Py<pyo3::types::PyDict>> {
98 crate::python::params::params_to_pydict(py, self)
99 }
100}
101
102impl Deref for Params {
103 type Target = IndexMap<String, Value>;
104
105 fn deref(&self) -> &Self::Target {
106 &self.0
107 }
108}
109
110impl DerefMut for Params {
111 fn deref_mut(&mut self) -> &mut Self::Target {
112 &mut self.0
113 }
114}
115
116impl<'a> IntoIterator for &'a Params {
117 type Item = (&'a String, &'a Value);
118 type IntoIter = indexmap::map::Iter<'a, String, Value>;
119
120 fn into_iter(self) -> Self::IntoIter {
121 self.0.iter()
122 }
123}
124
125#[cfg(feature = "python")]
126pub fn from_pydict(
136 py: pyo3::Python<'_>,
137 dict: pyo3::Py<pyo3::types::PyDict>,
138) -> pyo3::PyResult<Option<Params>> {
139 crate::python::params::pydict_to_params(py, dict)
140}
141
142#[cfg(test)]
143mod tests {
144 use rstest::*;
145 use serde_json::json;
146
147 use super::Params;
148
149 fn create_test_params() -> Params {
150 let mut params = Params::new();
151 params.insert("u64_val".to_string(), json!(42u64));
152 params.insert("i64_val".to_string(), json!(-100i64));
153 params.insert("usize_val".to_string(), json!(5u64));
154 params.insert("str_val".to_string(), json!("hello"));
155 params.insert("bool_val".to_string(), json!(true));
156 params.insert("f64_val".to_string(), json!(2.5));
157 params
158 }
159
160 #[rstest]
161 fn test_params_option_get_u64() {
162 let params = Some(create_test_params());
163 assert_eq!(params.as_ref().and_then(|p| p.get_u64("u64_val")), Some(42));
164 assert_eq!(params.as_ref().and_then(|p| p.get_u64("missing")), None);
165 assert_eq!(params.as_ref().and_then(|p| p.get_u64("str_val")), None);
166 }
167
168 #[rstest]
169 fn test_params_option_get_i64() {
170 let params = Some(create_test_params());
171 assert_eq!(
172 params.as_ref().and_then(|p| p.get_i64("i64_val")),
173 Some(-100)
174 );
175 assert_eq!(params.as_ref().and_then(|p| p.get_i64("missing")), None);
176 }
177
178 #[rstest]
179 fn test_params_option_get_usize() {
180 let params = Some(create_test_params());
181 assert_eq!(
182 params.as_ref().and_then(|p| p.get_usize("usize_val")),
183 Some(5)
184 );
185 assert_eq!(params.as_ref().and_then(|p| p.get_usize("missing")), None);
186 }
187
188 #[rstest]
189 fn test_params_option_get_str() {
190 let params = Some(create_test_params());
191 assert_eq!(
192 params.as_ref().and_then(|p| p.get_str("str_val")),
193 Some("hello")
194 );
195 assert_eq!(params.as_ref().and_then(|p| p.get_str("missing")), None);
196 assert_eq!(params.as_ref().and_then(|p| p.get_str("u64_val")), None);
197 }
198
199 #[rstest]
200 fn test_params_option_get_bool() {
201 let params = Some(create_test_params());
202 assert_eq!(
203 params.as_ref().and_then(|p| p.get_bool("bool_val")),
204 Some(true)
205 );
206 assert_eq!(params.as_ref().and_then(|p| p.get_bool("missing")), None);
207 }
208
209 #[rstest]
210 fn test_params_option_get_f64() {
211 let params = Some(create_test_params());
212 assert_eq!(
213 params.as_ref().and_then(|p| p.get_f64("f64_val")),
214 Some(2.5)
215 );
216 assert_eq!(params.as_ref().and_then(|p| p.get_f64("missing")), None);
217 }
218
219 #[rstest]
220 fn test_params_option_none() {
221 let params: Option<Params> = None;
222 assert_eq!(params.as_ref().and_then(|p| p.get_u64("any")), None);
223 assert_eq!(params.as_ref().and_then(|p| p.get_str("any")), None);
224 }
225
226 #[rstest]
227 fn test_params_ref_get_u64() {
228 let params = create_test_params();
229 assert_eq!(params.get_u64("u64_val"), Some(42));
230 assert_eq!(params.get_u64("missing"), None);
231 }
232
233 #[rstest]
234 fn test_params_ref_get_usize() {
235 let params = create_test_params();
236 assert_eq!(params.get_usize("usize_val"), Some(5));
237 assert_eq!(params.get_usize("missing"), None);
238 }
239
240 #[rstest]
241 fn test_params_ref_get_str() {
242 let params = create_test_params();
243 assert_eq!(params.get_str("str_val"), Some("hello"));
244 assert_eq!(params.get_str("missing"), None);
245 }
246
247 #[rstest]
248 fn test_submit_tries_pattern() {
249 let mut params = Params::new();
250 params.insert("submit_tries".to_string(), json!(3u64));
251 let cmd_params = Some(params);
252
253 let submit_tries = cmd_params
254 .as_ref()
255 .and_then(|p| p.get_usize("submit_tries"))
256 .filter(|&n| n > 0);
257
258 assert_eq!(submit_tries, Some(3));
259 }
260
261 #[rstest]
262 fn test_submit_tries_pattern_zero_filtered() {
263 let mut params = Params::new();
264 params.insert("submit_tries".to_string(), json!(0u64));
265 let cmd_params = Some(params);
266
267 let submit_tries = cmd_params
268 .as_ref()
269 .and_then(|p| p.get_usize("submit_tries"))
270 .filter(|&n| n > 0);
271
272 assert_eq!(submit_tries, None);
273 }
274
275 #[rstest]
276 fn test_submit_tries_pattern_missing() {
277 let cmd_params: Option<Params> = None;
278
279 let submit_tries = cmd_params
280 .as_ref()
281 .and_then(|p| p.get_usize("submit_tries"))
282 .filter(|&n| n > 0);
283
284 assert_eq!(submit_tries, None);
285 }
286}