1 | use std::{ |
2 | collections::HashMap, |
3 | convert::TryFrom, |
4 | fmt::{Display, Write}, |
5 | hash::BuildHasher, |
6 | }; |
7 | |
8 | use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer}; |
9 | use static_assertions::assert_impl_all; |
10 | |
11 | use 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)] |
20 | pub 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 | |
28 | assert_impl_all!(Dict<'_, '_>: Send, Sync, Unpin); |
29 | |
30 | impl<'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 | |
145 | impl 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 | |
151 | pub(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 | |
184 | impl<'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 |
199 | impl<'k, 'v, K, V, H> TryFrom<Dict<'k, 'v>> for HashMap<K, V, H> |
200 | where |
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 |
236 | impl<'k, 'v, K, V, H> From<HashMap<K, V, H>> for Dict<'k, 'v> |
237 | where |
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)] |
266 | struct DictEntry<'k, 'v> { |
267 | key: Value<'k>, |
268 | value: Value<'v>, |
269 | } |
270 | |
271 | impl<'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 | |
280 | impl<'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 | |
295 | fn 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 | |