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, type_annotate: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: ::SerializeSeq = serializer.serialize_seq(len:Some(self.entries.len()))?;
190 for entry: &DictEntry<'_, '_> 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: Vec> = valueimpl Iterator>
244 .into_iter()
245 .map(|(key: K, value: V)| DictEntry {
246 key: Value::new(key),
247 value: Value::new(value),
248 })
249 .collect();
250 let key_signature: Signature<'_> = K::signature();
251 let value_signature: Signature<'_> = V::signature();
252 let signature: 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: ::SerializeStruct = serializer.serialize_struct(name:"zvariant::DictEntry", len:2)?;
286 self.key
287 .serialize_value_as_struct_field(name:"zvariant::DictEntry::Key", &mut entry)?;
288 self.value
289 .serialize_value_as_struct_field(name:"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(signature:format!("a{{{key_signature}{value_signature}}}",))
300}
301