kaon/common/
map.rs

1use core::{borrow, fmt};
2use std::{
3    collections::HashMap,
4    hash::{self, BuildHasherDefault},
5    rc::Rc,
6};
7
8use super::{FromValue, Named, ToValue};
9use crate::Value;
10
11pub type Keys<'a> = std::collections::hash_map::Keys<'a, String, Value>;
12
13pub type Values<'a> = std::collections::hash_map::Values<'a, String, Value>;
14
15/// A struct representing a map at runtime.
16///
17/// # Examples
18///
19/// ```rust
20/// # fn main() -> kaon::Result<()> {
21/// let mut map = kaon::common::Map::new();
22/// assert_eq!(map.len(), 0);
23///
24/// map.insert_value::<i32>(String::from("x"), 32);
25/// map.insert_value::<&str>(String::from("y"), "hello");
26/// assert_eq!(map.len(), 2);
27///
28/// assert_eq!(Some(32), map.get_value("x"));
29/// assert_eq!(Some(String::from("hello")), map.get_value("y"));
30///
31/// # Ok(())
32/// # }
33/// ```
34#[derive(Default, Clone)]
35pub struct Map {
36    inner: Rc<HashMap<String, Value, BuildHasherDefault<ahash::AHasher>>>,
37}
38
39impl Map {
40    /// Create a new empty [`Map`].
41    pub fn new() -> Self {
42        Self {
43            inner: Rc::new(HashMap::default()),
44        }
45    }
46
47    /// Create a new empty [`Map`] with the specified capacity.
48    pub fn with_capacity(cap: usize) -> Self {
49        Self {
50            inner: Rc::new(HashMap::with_capacity_and_hasher(
51                cap,
52                BuildHasherDefault::default(),
53            )),
54        }
55    }
56
57    pub fn make_mut(&mut self) -> &mut HashMap<String, Value, BuildHasherDefault<ahash::AHasher>> {
58        Rc::make_mut(&mut self.inner)
59    }
60
61    /// Return a reference to the value corresponding to the key.
62    pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&Value>
63    where
64        String: borrow::Borrow<Q>,
65        Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
66    {
67        self.inner.get(k)
68    }
69
70    /// Return a mutable reference to the value corresponding to the key
71    pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut Value>
72    where
73        String: borrow::Borrow<Q>,
74        Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
75    {
76        self.make_mut().get_mut(k)
77    }
78
79    /// Return a reference to the value corresponding to the key, converting the
80    /// value as needed with the [`FromValue`] trait.
81    pub fn get_value<Q: ?Sized, T>(&self, k: &Q) -> Option<T>
82    where
83        String: borrow::Borrow<Q>,
84        Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
85        T: FromValue,
86    {
87        let v = match self.inner.get(k) {
88            Some(v) => v.clone(),
89            None => return None,
90        };
91
92        Some(T::from_value(v).unwrap())
93    }
94
95    /// Insert a key-value pair into the [`Map`].
96    pub fn insert(&mut self, k: String, v: Value) {
97        self.make_mut().insert(k, v);
98    }
99
100    /// Insert a key-value pair into the map, converting the value
101    /// using the [`ToValue`] trait.
102    pub fn insert_value<V>(&mut self, k: String, v: V)
103    where
104        V: ToValue,
105    {
106        self.make_mut().insert(k, v.to_value());
107    }
108
109    /// Removes a key from the [`Map`], returning the value at the key.
110    pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<Value>
111    where
112        String: borrow::Borrow<Q>,
113        Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
114    {
115        self.make_mut().remove(k)
116    }
117
118    /// Return the number of key-value pairs in the [`Map`].
119    pub fn len(&self) -> usize {
120        self.inner.len()
121    }
122
123    /// Returns `true` if the inner [`HashMap`] is empty, otherwise `false`.
124    pub fn is_empty(&self) -> bool {
125        self.inner.is_empty()
126    }
127
128    /// An iterator visiting all keys in an arbitrary order.
129    pub fn keys(&self) -> Keys<'_> {
130        self.inner.keys()
131    }
132
133    /// An iterator visiting all values in an arbitrary order.
134    pub fn values(&self) -> Values<'_> {
135        self.inner.values()
136    }
137
138    pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool
139    where
140        String: borrow::Borrow<Q>,
141        Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
142    {
143        self.inner.contains_key(k)
144    }
145}
146
147impl FromValue for Map {
148    fn from_value(value: Value) -> Result<Self, String> {
149        match value {
150            Value::Map(map) => Ok(map),
151            _ => {
152                Err("cannot coerce map from value".into())
153            }
154        }
155    }
156}
157
158impl ToValue for Map {
159    fn to_value(self) -> Value {
160        Value::Map(self)
161    }
162}
163
164impl fmt::Debug for Map {
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        f.debug_map().entries(self.inner.iter()).finish()
167    }
168}
169
170impl fmt::Display for Map {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        f.write_str("{")?;
173
174        for (pos, (key, value)) in self.inner.iter().enumerate() {
175            f.write_fmt(format_args!("{key}: {value}"))?;
176
177            if pos != self.inner.len() {
178                f.write_str(", ")?;
179            }
180        }
181        f.write_str("}")
182    }
183}
184
185impl PartialEq for Map {
186    fn eq(&self, other: &Self) -> bool {
187        self.len() == other.len() && self.inner == other.inner
188    }
189}
190
191impl PartialOrd for Map {
192    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
193        self.values().partial_cmp(other.values())
194    }
195}
196
197impl Named for Map {
198    const NAME: &'static str = "Map";
199}