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}