zvariant/
dict.rs

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