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