zvariant/
dict.rs

1use std::{
2    collections::{BTreeMap, HashMap},
3    fmt::{Display, Write},
4    hash::{BuildHasher, Hash},
5};
6
7use serde::ser::{Serialize, SerializeMap, Serializer};
8use static_assertions::assert_impl_all;
9
10use crate::{value_display_fmt, Basic, DynamicType, Error, Signature, Type, Value};
11
12/// A helper type to wrap dictionaries in a [`Value`].
13///
14/// API is provided to convert from, and to a [`HashMap`].
15///
16/// [`Value`]: enum.Value.html#variant.Dict
17/// [`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
18#[derive(Debug, Hash, PartialEq, PartialOrd, Eq, Ord)]
19pub struct Dict<'k, 'v> {
20    map: BTreeMap<Value<'k>, Value<'v>>,
21    key_signature: Signature<'k>,
22    value_signature: Signature<'v>,
23    // should use a separate lifetime or everything should use the same but API break.
24    signature: Signature<'k>,
25}
26
27assert_impl_all!(Dict<'_, '_>: Send, Sync, Unpin);
28
29impl<'k, 'v> Dict<'k, 'v> {
30    /// Create a new empty `Dict`, given the signature of the keys and values.
31    pub fn new(key_signature: Signature<'k>, value_signature: Signature<'v>) -> Self {
32        let signature = create_signature(&key_signature, &value_signature);
33
34        Self {
35            map: BTreeMap::new(),
36            key_signature,
37            value_signature,
38            signature,
39        }
40    }
41
42    /// Append `key` and `value` as a new entry.
43    ///
44    /// # Errors
45    ///
46    /// * if [`key.value_signature()`] doesn't match the key signature `self` was created for.
47    /// * if [`value.value_signature()`] doesn't match the value signature `self` was created for.
48    ///
49    /// [`key.value_signature()`]: enum.Value.html#method.value_signature
50    /// [`value.value_signature()`]: enum.Value.html#method.value_signature
51    pub fn append<'kv: 'k, 'vv: 'v>(
52        &mut self,
53        key: Value<'kv>,
54        value: Value<'vv>,
55    ) -> Result<(), Error> {
56        check_child_value_signature!(self.key_signature, key.value_signature(), "key");
57        check_child_value_signature!(self.value_signature, value.value_signature(), "value");
58
59        self.map.insert(key, value);
60
61        Ok(())
62    }
63
64    /// Add a new entry.
65    pub fn add<K, V>(&mut self, key: K, value: V) -> Result<(), Error>
66    where
67        K: Basic + Into<Value<'k>> + Ord,
68        V: Into<Value<'v>> + DynamicType,
69    {
70        check_child_value_signature!(self.key_signature, K::signature(), "key");
71        check_child_value_signature!(self.value_signature, value.dynamic_signature(), "value");
72
73        self.map.insert(Value::new(key), Value::new(value));
74
75        Ok(())
76    }
77
78    /// Get the value for the given key.
79    pub fn get<'d, K, V>(&'d self, key: &'k K) -> Result<Option<V>, Error>
80    where
81        'd: 'k + 'v,
82        &'k K: TryInto<Value<'k>>,
83        <&'k K as TryInto<Value<'k>>>::Error: Into<crate::Error>,
84        V: TryFrom<&'v Value<'v>>,
85        <V as TryFrom<&'v Value<'v>>>::Error: Into<crate::Error>,
86    {
87        let key: Value<'_> = key.try_into().map_err(Into::into)?;
88
89        self.map.get(&key).map(|v| v.downcast_ref()).transpose()
90    }
91
92    /// Get the signature of this `Dict`.
93    ///
94    /// NB: This method potentially allocates and copies. Use [`full_signature`] if you'd like to
95    /// avoid that.
96    ///
97    /// [`full_signature`]: #method.full_signature
98    pub fn signature(&self) -> Signature<'static> {
99        self.signature.to_owned()
100    }
101
102    /// Get the signature of this `Dict`.
103    pub fn full_signature(&self) -> &Signature<'_> {
104        &self.signature
105    }
106
107    pub(crate) fn try_to_owned(&self) -> crate::Result<Dict<'static, 'static>> {
108        Ok(Dict {
109            key_signature: self.key_signature.to_owned(),
110            value_signature: self.value_signature.to_owned(),
111            signature: self.signature.to_owned(),
112            map: self
113                .map
114                .iter()
115                .map(|(k, v)| {
116                    Ok((
117                        k.try_to_owned().map(Into::into)?,
118                        v.try_to_owned().map(Into::into)?,
119                    ))
120                })
121                .collect::<crate::Result<_>>()?,
122        })
123    }
124
125    /// Try to clone the `Dict`.
126    pub fn try_clone(&self) -> Result<Self, Error> {
127        let entries = self
128            .map
129            .iter()
130            .map(|(k, v)| Ok((k.try_clone()?, v.try_clone()?)))
131            .collect::<Result<_, crate::Error>>()?;
132
133        Ok(Self {
134            map: entries,
135            key_signature: self.key_signature.clone(),
136            value_signature: self.value_signature.clone(),
137            signature: self.signature.clone(),
138        })
139    }
140
141    /// Create a new empty `Dict`, given the complete signature.
142    pub(crate) fn new_full_signature<'s: 'k + 'v>(signature: Signature<'s>) -> Self {
143        let key_signature = signature.slice(2..3);
144        let value_signature = signature.slice(3..signature.len() - 1);
145
146        Self {
147            map: BTreeMap::new(),
148            key_signature,
149            value_signature,
150            signature,
151        }
152    }
153
154    pub fn iter(&self) -> impl Iterator<Item = (&Value<'k>, &Value<'v>)> {
155        self.map.iter()
156    }
157
158    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Value<'k>, &mut Value<'v>)> {
159        self.map.iter_mut()
160    }
161
162    // TODO: Provide more API like https://docs.rs/toml/0.5.5/toml/map/struct.Map.html
163}
164
165impl Display for Dict<'_, '_> {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        dict_display_fmt(self, f, true)
168    }
169}
170
171impl<'k, 'v> IntoIterator for Dict<'k, 'v> {
172    type Item = (Value<'k>, Value<'v>);
173    type IntoIter = <BTreeMap<Value<'k>, Value<'v>> as IntoIterator>::IntoIter;
174
175    fn into_iter(self) -> Self::IntoIter {
176        self.map.into_iter()
177    }
178}
179
180pub(crate) fn dict_display_fmt(
181    dict: &Dict<'_, '_>,
182    f: &mut std::fmt::Formatter<'_>,
183    type_annotate: bool,
184) -> std::fmt::Result {
185    if dict.map.is_empty() {
186        if type_annotate {
187            write!(f, "@{} ", dict.full_signature())?;
188        }
189        f.write_str("{}")?;
190    } else {
191        f.write_char('{')?;
192
193        // Annotate only the first entry as the rest will be of the same type.
194        let mut type_annotate = type_annotate;
195
196        for (i, (key, value)) in dict.map.iter().enumerate() {
197            value_display_fmt(key, f, type_annotate)?;
198            f.write_str(": ")?;
199            value_display_fmt(value, f, type_annotate)?;
200            type_annotate = false;
201
202            if i + 1 < dict.map.len() {
203                f.write_str(", ")?;
204            }
205        }
206
207        f.write_char('}')?;
208    }
209
210    Ok(())
211}
212
213impl<'k, 'v> Serialize for Dict<'k, 'v> {
214    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
215    where
216        S: Serializer,
217    {
218        let mut map = serializer.serialize_map(Some(self.map.len()))?;
219        for (key, value) in self.map.iter() {
220            key.serialize_value_as_dict_key(&mut map)?;
221            value.serialize_value_as_dict_value(&mut map)?;
222        }
223
224        map.end()
225    }
226}
227
228// Conversion of Dict to Map types
229macro_rules! from_dict {
230    ($ty:ident <K $(: $kbound1:ident $(+ $kbound2:ident)*)*, V $(, $typaram:ident)*>) => {
231        impl<'k, 'v, K, V $(, $typaram)*> TryFrom<Dict<'k, 'v>> for $ty<K, V $(, $typaram)*>
232        where
233            K: Basic + TryFrom<Value<'k>> $(+ $kbound1 $(+ $kbound2)*)*,
234            V: TryFrom<Value<'v>>,
235            K::Error: Into<crate::Error>,
236            V::Error: Into<crate::Error>,
237            $($typaram: BuildHasher + Default,)*
238        {
239            type Error = Error;
240
241            fn try_from(v: Dict<'k, 'v>) -> Result<Self, Self::Error> {
242                v.map.into_iter().map(|(key, value)| {
243                    let key = if let Value::Value(v) = key {
244                        K::try_from(*v)
245                    } else {
246                        K::try_from(key)
247                    }
248                    .map_err(Into::into)?;
249
250                    let value = if let Value::Value(v) = value {
251                        V::try_from(*v)
252                    } else {
253                        V::try_from(value)
254                    }
255                    .map_err(Into::into)?;
256
257                    Ok((key, value))
258                }).collect::<Result<_, _>>()
259            }
260        }
261    };
262}
263from_dict!(HashMap<K: Eq + Hash, V, H>);
264from_dict!(BTreeMap<K: Ord, V>);
265
266// TODO: this could be useful
267// impl<'d, 'k, 'v, K, V, H> TryFrom<&'d Dict<'k, 'v>> for HashMap<&'k K, &'v V, H>
268
269// Conversion of Hashmap to Dict
270macro_rules! to_dict {
271    ($ty:ident <K $(: $kbound1:ident $(+ $kbound2:ident)*)*, V $(, $typaram:ident)*>) => {
272        impl<'k, 'v, K, V $(, $typaram)*> From<$ty<K, V $(, $typaram)*>> for Dict<'k, 'v>
273        where
274            K: Type + Into<Value<'k>>,
275            V: Type + Into<Value<'v>>,
276            $($typaram: BuildHasher,)*
277        {
278            fn from(value: $ty<K, V $(, $typaram)*>) -> Self {
279                let entries = value
280                    .into_iter()
281                    .map(|(key, value)| (Value::new(key), Value::new(value)))
282                    .collect();
283                let key_signature = K::signature();
284                let value_signature = V::signature();
285                let signature = create_signature(&key_signature, &value_signature);
286
287                Self {
288                    map: entries,
289                    key_signature,
290                    value_signature,
291                    signature,
292                }
293            }
294        }
295    };
296}
297to_dict!(HashMap<K: Eq + Hash, V, H>);
298to_dict!(BTreeMap<K: Ord, V>);
299
300fn create_signature(
301    key_signature: &Signature<'_>,
302    value_signature: &Signature<'_>,
303) -> Signature<'static> {
304    Signature::from_string_unchecked(format!("a{{{key_signature}{value_signature}}}",))
305}