argmin/core/kv.rs
1// Copyright 2018-2024 argmin developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use std::collections::HashMap;
9use std::fmt;
10use std::fmt::{Debug, Display};
11
12#[cfg(feature = "serde1")]
13use serde::{Deserialize, Serialize};
14
15/// Types available for use in [`KV`](KV).
16///
17/// `Float`, `Int` and `Uint` are all 64bit. The corresponding 32bit variants must be
18/// be converted to 64 bit. Preferably the `From` impls are used to create a `KvValue`:
19///
20/// ```
21/// # use argmin::core::KvValue;
22/// let x: KvValue = 1u64.into();
23/// assert_eq!(x, KvValue::Uint(1u64));
24///
25/// let x: KvValue = 2u32.into();
26/// assert_eq!(x, KvValue::Uint(2u64));
27///
28/// let x: KvValue = 2i64.into();
29/// assert_eq!(x, KvValue::Int(2i64));
30///
31/// let x: KvValue = 2i32.into();
32/// assert_eq!(x, KvValue::Int(2i64));
33///
34/// let x: KvValue = 1.0f64.into();
35/// assert_eq!(x, KvValue::Float(1f64));
36///
37/// let x: KvValue = 1.0f32.into();
38/// assert_eq!(x, KvValue::Float(1f64));
39///
40/// let x: KvValue = true.into();
41/// assert_eq!(x, KvValue::Bool(true));
42///
43/// let x: KvValue = "a str".into();
44/// assert_eq!(x, KvValue::Str("a str".to_string()));
45///
46/// let x: KvValue = "a String".to_string().into();
47/// assert_eq!(x, KvValue::Str("a String".to_string()));
48/// ```
49#[derive(Clone, PartialEq, Debug)]
50#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
51pub enum KvValue {
52 /// Floating point values
53 Float(f64),
54 /// Signed integers
55 Int(i64),
56 /// Unsigned integers
57 Uint(u64),
58 /// Boolean values
59 Bool(bool),
60 /// Strings
61 Str(String),
62}
63
64impl KvValue {
65 /// Returns the kind of the `KvValue`
66 ///
67 /// # Examples
68 ///
69 /// ```
70 /// # use argmin::core::KvValue;
71 /// assert_eq!(KvValue::Float(1.0).kind(), "Float");
72 /// assert_eq!(KvValue::Int(1).kind(), "Int");
73 /// assert_eq!(KvValue::Uint(1).kind(), "Uint");
74 /// assert_eq!(KvValue::Bool(true).kind(), "Bool");
75 /// assert_eq!(KvValue::Str("string".to_string()).kind(), "Str");
76 /// ```
77 pub fn kind(&self) -> &'static str {
78 match self {
79 KvValue::Float(_) => "Float",
80 KvValue::Int(_) => "Int",
81 KvValue::Uint(_) => "Uint",
82 KvValue::Bool(_) => "Bool",
83 KvValue::Str(_) => "Str",
84 }
85 }
86
87 /// Extract float from `KvValue`
88 ///
89 /// Returns `Some(<float>)` if `KvValue` is of kind `Float`.
90 ///
91 /// **Note:** For `KvValue::Int` and `KvValue::Uint`integer values are cast to f64, therefore
92 /// this operation may be lossy!
93 ///
94 /// `KvValue::Bool` is turned into `1.0f64` if true and `0.0f64` if false.
95 ///
96 /// # Example
97 ///
98 /// ```
99 /// # use argmin::core::KvValue;
100 /// assert_eq!(KvValue::Float(1.0).get_float(), Some(1.0));
101 /// assert_eq!(KvValue::Int(1).get_float(), Some(1.0));
102 /// assert_eq!(KvValue::Uint(1).get_float(), Some(1.0));
103 /// assert_eq!(KvValue::Bool(true).get_float(), Some(1.0));
104 /// assert_eq!(KvValue::Str("not a number".to_string()).get_float(), None);
105 /// ```
106 pub fn get_float(&self) -> Option<f64> {
107 match self {
108 KvValue::Float(x) => Some(*x),
109 KvValue::Int(x) => Some(*x as f64),
110 KvValue::Uint(x) => Some(*x as f64),
111 KvValue::Bool(true) => Some(1.0),
112 KvValue::Bool(false) => Some(0.0),
113 _ => None,
114 }
115 }
116
117 /// Extract int from `KvValue`
118 ///
119 /// Returns `Some(<int>)` if `KvValue` is of kind `Int` and `None` otherwise.
120 ///
121 /// # Example
122 ///
123 /// ```
124 /// # use argmin::core::KvValue;
125 /// assert_eq!(KvValue::Int(1).get_int(), Some(1i64));
126 /// assert_eq!(KvValue::Float(1.0).get_int(), None);
127 /// assert_eq!(KvValue::Uint(1).get_int(), None);
128 /// assert_eq!(KvValue::Bool(true).get_int(), None);
129 /// assert_eq!(KvValue::Str("not an int".to_string()).get_int(), None);
130 /// ```
131 pub fn get_int(&self) -> Option<i64> {
132 if let KvValue::Int(x) = *self {
133 Some(x)
134 } else {
135 None
136 }
137 }
138
139 /// Extract unsigned int from `KvValue`
140 ///
141 /// Returns `Some(<unsigned int>)` if `KvValue` is of kind `Uint` and `None` otherwise.
142 ///
143 /// # Example
144 ///
145 /// ```
146 /// # use argmin::core::KvValue;
147 /// assert_eq!(KvValue::Uint(1).get_uint(), Some(1u64));
148 /// assert_eq!(KvValue::Int(1).get_uint(), None);
149 /// assert_eq!(KvValue::Float(1.0).get_uint(), None);
150 /// assert_eq!(KvValue::Bool(true).get_uint(), None);
151 /// assert_eq!(KvValue::Str("not an uint".to_string()).get_uint(), None);
152 /// ```
153 pub fn get_uint(&self) -> Option<u64> {
154 if let KvValue::Uint(x) = *self {
155 Some(x)
156 } else {
157 None
158 }
159 }
160
161 /// Extract bool from `KvValue`
162 ///
163 /// Returns `Some(<bool>)` if `KvValue` is of kind `Bool` and `None` otherwise.
164 ///
165 /// # Example
166 ///
167 /// ```
168 /// # use argmin::core::KvValue;
169 /// assert_eq!(KvValue::Bool(true).get_bool(), Some(true));
170 /// assert_eq!(KvValue::Float(1.0).get_bool(), None);
171 /// assert_eq!(KvValue::Int(1).get_bool(), None);
172 /// assert_eq!(KvValue::Uint(1).get_bool(), None);
173 /// assert_eq!(KvValue::Str("not a bool".to_string()).get_bool(), None);
174 /// ```
175 pub fn get_bool(&self) -> Option<bool> {
176 if let KvValue::Bool(x) = *self {
177 Some(x)
178 } else {
179 None
180 }
181 }
182
183 /// Extract String from `KvValue`
184 ///
185 /// Returns `Some(<string>)` if `KvValue` is of kind `Str` and `None` otherwise.
186 ///
187 /// # Example
188 ///
189 /// ```
190 /// # use argmin::core::KvValue;
191 /// assert_eq!(KvValue::Str("a string".to_string()).get_string(), Some("a string".to_string()));
192 /// assert_eq!(KvValue::Float(1.0).get_string(), None);
193 /// assert_eq!(KvValue::Int(1).get_string(), None);
194 /// assert_eq!(KvValue::Uint(1).get_string(), None);
195 /// assert_eq!(KvValue::Bool(true).get_string(), None);
196 /// ```
197 pub fn get_string(&self) -> Option<String> {
198 if let KvValue::Str(x) = self {
199 Some(x.clone())
200 } else {
201 None
202 }
203 }
204
205 /// Get String representation of `KvValue`
206 ///
207 /// # Example
208 ///
209 /// ```
210 /// # use argmin::core::KvValue;
211 /// assert_eq!(KvValue::Str("a string".to_string()).as_string(), "a string".to_string());
212 /// assert_eq!(KvValue::Float(1.0).as_string(), "1".to_string());
213 /// assert_eq!(KvValue::Int(1).as_string(), "1".to_string());
214 /// assert_eq!(KvValue::Uint(1).as_string(), "1".to_string());
215 /// assert_eq!(KvValue::Bool(true).as_string(), "true".to_string());
216 /// ```
217 pub fn as_string(&self) -> String {
218 match self {
219 KvValue::Str(x) => x.clone(),
220 KvValue::Float(x) => format!("{x}"),
221 KvValue::Bool(x) => format!("{x}"),
222 KvValue::Int(x) => format!("{x}"),
223 KvValue::Uint(x) => format!("{x}"),
224 }
225 }
226}
227
228impl From<f64> for KvValue {
229 fn from(x: f64) -> KvValue {
230 KvValue::Float(x)
231 }
232}
233
234impl From<f32> for KvValue {
235 fn from(x: f32) -> KvValue {
236 KvValue::Float(f64::from(x))
237 }
238}
239
240impl From<i64> for KvValue {
241 fn from(x: i64) -> KvValue {
242 KvValue::Int(x)
243 }
244}
245
246impl From<u64> for KvValue {
247 fn from(x: u64) -> KvValue {
248 KvValue::Uint(x)
249 }
250}
251
252impl From<i32> for KvValue {
253 fn from(x: i32) -> KvValue {
254 KvValue::Int(i64::from(x))
255 }
256}
257
258impl From<u32> for KvValue {
259 fn from(x: u32) -> KvValue {
260 KvValue::Uint(u64::from(x))
261 }
262}
263
264impl From<bool> for KvValue {
265 fn from(x: bool) -> KvValue {
266 KvValue::Bool(x)
267 }
268}
269
270impl From<String> for KvValue {
271 fn from(x: String) -> KvValue {
272 KvValue::Str(x)
273 }
274}
275
276impl<'a> From<&'a str> for KvValue {
277 fn from(x: &'a str) -> KvValue {
278 KvValue::Str(x.to_string())
279 }
280}
281
282impl Display for KvValue {
283 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284 match self {
285 KvValue::Float(x) => write!(f, "{x}")?,
286 KvValue::Int(x) => write!(f, "{x}")?,
287 KvValue::Uint(x) => write!(f, "{x}")?,
288 KvValue::Bool(x) => write!(f, "{x}")?,
289 KvValue::Str(x) => write!(f, "{x}")?,
290 };
291 Ok(())
292 }
293}
294
295/// A simple key-value storage
296///
297/// Keeps pairs of `(&'static str, KvValue)` and is used to pass key-value pairs to
298/// [`Observers`](`crate::core::observers`) in each iteration of an optimization algorithm.
299/// Typically constructed using the [`kv!`](`crate::kv`) macro.
300///
301/// # Example
302///
303/// ```
304/// use argmin::kv;
305///
306/// let kv = kv!(
307/// "key1" => "value1";
308/// "key2" => "value2";
309/// "key3" => 1234;
310/// );
311/// # assert_eq!(kv.kv.len(), 3);
312/// # assert_eq!(format!("{}", kv.get("key1").unwrap()), "value1");
313/// # assert_eq!(format!("{}", kv.get("key2").unwrap()), "value2");
314/// # assert_eq!(format!("{}", kv.get("key3").unwrap()), "1234");
315/// ```
316#[derive(Clone, Default, PartialEq)]
317#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
318pub struct KV {
319 /// The actual key value storage
320 pub kv: HashMap<String, KvValue>,
321}
322
323impl Debug for KV {
324 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325 writeln!(f, "{self}")?;
326 Ok(())
327 }
328}
329impl Display for KV {
330 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331 writeln!(f, "KV")?;
332 for (key, val) in self.kv.iter() {
333 writeln!(f, " {key}: {val}")?;
334 }
335 Ok(())
336 }
337}
338
339impl KV {
340 /// Constructor a new empty `KV`
341 ///
342 /// # Example
343 ///
344 /// ```
345 /// # use argmin::core::KV;
346 /// #
347 /// let kv = KV::new();
348 /// # assert_eq!(kv.kv.len(), 0);
349 /// ```
350 pub fn new() -> Self {
351 KV { kv: HashMap::new() }
352 }
353
354 /// Insert a key-value pair
355 ///
356 /// # Example
357 ///
358 /// ```
359 /// # use argmin::core::{KV, KvValue};
360 /// let mut kv = KV::new();
361 /// kv.insert("key1", KvValue::Str("value".to_string()));
362 /// kv.insert("key2", KvValue::Int(1234));
363 /// # assert_eq!(kv.kv.len(), 2);
364 /// # assert_eq!(format!("{}", kv.get("key1").unwrap()), "value");
365 /// # assert_eq!(format!("{}", kv.get("key2").unwrap()), "1234");
366 /// ```
367 pub fn insert<T: AsRef<str>>(&mut self, key: T, val: KvValue) -> &mut Self {
368 self.kv.insert(key.as_ref().into(), val);
369 self
370 }
371
372 /// Retrieve an element from the KV by key
373 ///
374 /// Returns `Some(<reference to KvValue>)` if `key` is present and `None` otherwise.
375 ///
376 /// # Example
377 ///
378 /// ```
379 /// # use argmin::core::{KV, KvValue};
380 /// let mut kv1 = KV::new();
381 /// kv1.insert("key1", KvValue::Float(12.0));
382 ///
383 /// assert_eq!(kv1.get("key1"), Some(&KvValue::Float(12.0)));
384 /// assert_eq!(kv1.get("non_existing"), None);
385 /// ```
386 pub fn get<T: AsRef<str>>(&self, key: T) -> Option<&KvValue> {
387 self.kv.get(key.as_ref())
388 }
389
390 /// Returns all available keys and their `KvValue` kind
391 ///
392 /// # Example
393 ///
394 /// ```
395 /// # use argmin::core::{KV, KvValue};
396 /// let mut kv1 = KV::new();
397 /// kv1.insert("key1", KvValue::Str("value1".to_string()));
398 ///
399 /// assert_eq!(kv1.keys(), vec![("key1".to_string(), "Str")]);
400 /// ```
401 pub fn keys(&self) -> Vec<(String, &'static str)> {
402 self.kv.iter().map(|(k, v)| (k.clone(), v.kind())).collect()
403 }
404
405 /// Merge with another `KV`
406 ///
407 /// # Example
408 ///
409 /// ```
410 /// # use argmin::core::{KV, KvValue};
411 /// let mut kv1 = KV::new();
412 /// kv1.insert("key1", KvValue::Str("value1".to_string()));
413 /// # assert_eq!(kv1.kv.len(), 1);
414 /// # assert_eq!(format!("{}", kv1.get("key1").unwrap()), "value1");
415 ///
416 /// let mut kv2 = KV::new();
417 /// kv2.insert("key2", KvValue::Str("value2".to_string()));
418 /// # assert_eq!(kv2.kv.len(), 1);
419 /// # assert_eq!(format!("{}", kv2.get("key2").unwrap()), "value2");
420 ///
421 /// let kv1 = kv1.merge(kv2);
422 /// # assert_eq!(kv1.kv.len(), 2);
423 /// # assert_eq!(format!("{}", kv1.get("key1").unwrap()), "value1");
424 /// # assert_eq!(format!("{}", kv1.get("key2").unwrap()), "value2");
425 /// ```
426 #[must_use]
427 pub fn merge(mut self, other: KV) -> Self {
428 self.kv.extend(other.kv);
429 self
430 }
431}
432
433impl std::iter::FromIterator<(&'static str, KvValue)> for KV {
434 fn from_iter<I: IntoIterator<Item = (&'static str, KvValue)>>(iter: I) -> Self {
435 let mut c = KV::new();
436 for i in iter {
437 c.insert(i.0, i.1);
438 }
439 c
440 }
441}
442
443impl std::iter::Extend<(&'static str, KvValue)> for KV {
444 fn extend<I: IntoIterator<Item = (&'static str, KvValue)>>(&mut self, iter: I) {
445 for i in iter {
446 self.insert(i.0, i.1);
447 }
448 }
449}