| 1 | use std::{ |
| 2 | collections::{BTreeMap, HashMap}, |
| 3 | fmt::{Display, Write}, |
| 4 | hash::{BuildHasher, Hash}, |
| 5 | }; |
| 6 | |
| 7 | use serde::ser::{Serialize, SerializeMap, Serializer}; |
| 8 | use static_assertions::assert_impl_all; |
| 9 | |
| 10 | use 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)] |
| 19 | pub 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 | |
| 27 | assert_impl_all!(Dict<'_, '_>: Send, Sync, Unpin); |
| 28 | |
| 29 | impl<'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 | |
| 165 | impl Display for Dict<'_, '_> { |
| 166 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 167 | dict_display_fmt(self, f, type_annotate:true) |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | impl<'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 | |
| 180 | pub(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 | |
| 213 | impl<'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: ::SerializeMap = serializer.serialize_map(len:Some(self.map.len()))?; |
| 219 | for (key: &Value<'k>, value: &Value<'v>) 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 |
| 229 | macro_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 | } |
| 263 | from_dict!(HashMap<K: Eq + Hash, V, H>); |
| 264 | from_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 |
| 270 | macro_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 | } |
| 297 | to_dict!(HashMap<K: Eq + Hash, V, H>); |
| 298 | to_dict!(BTreeMap<K: Ord, V>); |
| 299 | |
| 300 | fn create_signature( |
| 301 | key_signature: &Signature<'_>, |
| 302 | value_signature: &Signature<'_>, |
| 303 | ) -> Signature<'static> { |
| 304 | Signature::from_string_unchecked(signature:format!("a {{{key_signature}{value_signature}}}" ,)) |
| 305 | } |
| 306 | |