url_cleaner_engine/types/
map.rs

1//! `HashMap<Option<String>, T>` you can index with `Option<&T>`.
2//!
3//! Also has an optional fallback value for keys not otherwise in the map.
4
5use std::collections::{HashMap, HashSet};
6
7use serde::{Serialize, Deserialize};
8use serde_with::{serde_as, MapPreventDuplicates, SetPreventDuplicates};
9
10use crate::util::*;
11
12/// Allows semantics similar to `HashMap<Option<String>, _>` without having to convert `Option<&str>`s to `Option<String>`s.
13///
14/// Also has [`Self::else`] to specify a return value when a key isn't otherwise found.
15#[serde_as]
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Suitability)]
17#[serde(deny_unknown_fields)]
18pub struct Map<T> {
19    /// The map from [`Some`] to `T`.
20    #[serde_as(as = "MapPreventDuplicates<_, _>")]
21    pub map: HashMap<String, T>,
22    /// The map from [`None`] to `T`.
23    #[serde(default = "Option::default", skip_serializing_if = "Option::is_none")]
24    pub if_none: Option<Box<T>>,
25    /// The value to return when a value is otherwise not found.
26    #[serde(default = "Option::default", skip_serializing_if = "Option::is_none")]
27    pub r#else: Option<Box<T>>
28}
29
30impl<T> Map<T> {
31    /// [`HashMap::with_capacity`].
32    pub fn with_capacity(capacity: usize) -> Self {
33        Map {
34            map    : HashMap::with_capacity(capacity),
35            if_none: None,
36            r#else : None
37        }
38    }
39
40    /// If [`Some`], returns the corresponding value from [`Self::map`].
41    ///
42    /// If [`None`], returns the value of [`Self::if_none`].
43    ///
44    /// If either of the above return [`None`], returns the value of [`Self::else`].
45    pub fn get<U: AsRef<str>>(&self, key: Option<U>) -> Option<&T> {
46        match key {
47            Some(key) => self.map.get(key.as_ref()),
48            None => self.if_none.as_deref()
49        }.or(self.r#else.as_deref())
50    }
51}
52
53impl<T> Default for Map<T> {
54    fn default() -> Self {
55        Self {
56            map    : Default::default(),
57            if_none: Default::default(),
58            r#else : Default::default()
59        }
60    }
61}
62
63/// Rules for updating a [`Map`].
64#[serde_as]
65#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Suitability)]
66pub struct MapDiff<T> {
67    /// Values to insert/replace into [`Map::map`].
68    #[serde_as(as = "MapPreventDuplicates<_, _>")]
69    #[serde(default, bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))]
70    pub insert: HashMap<String, T>,
71    /// Values to remove from [`Map::map`].
72    #[serde_as(as = "SetPreventDuplicates<_>")]
73    #[serde(default)]
74    pub remove: HashSet<String>
75}
76
77impl<T> MapDiff<T> {
78    /// Applies the diff.
79    ///
80    /// If you want to apply `self` multiple times, use [`Self::apply_multiple`] as it's slightly faster than [`Clone::clone`]ing this then using [`Self::apply_once`] on each clone.
81    pub fn apply_once(self, to: &mut Map<T>) {
82        to.map.extend(self.insert);
83        to.map.retain(|k, _| !self.remove.contains(k));
84    }
85}
86
87impl<T: Clone> MapDiff<T> {
88    /// Applies the diff.
89    ///
90    /// If you only want to apply `self` once, use [`Self::apply_once`].
91    pub fn apply_multiple(&self, to: &mut Map<T>) {
92        to.map.extend(self.insert.iter().map(|(k, v)| (k.clone(), v.clone())));
93        to.map.retain(|k, _| !self.remove.contains(k));
94    }
95}
96
97impl<T, const N: usize> From<[(String, T); N]> for Map<T> {
98    fn from(value: [(String, T); N]) -> Self {
99        Self {
100            map: value.into(),
101            if_none: None,
102            r#else: None
103        }
104    }
105}
106
107impl<T, const N: usize> From<[(Option<String>, T); N]> for Map<T> {
108    fn from(value: [(Option<String>, T); N]) -> Self {
109        let mut ret = Self {
110            map: HashMap::with_capacity(N),
111            if_none: None,
112            r#else: None
113        };
114
115        for (k, v) in value {
116            match k {
117                Some(k) => {ret.map.insert(k, v);},
118                None => ret.if_none = Some(Box::new(v))
119            }
120        }
121
122        ret
123    }
124}
125
126impl<T> From<HashMap<String, T>> for Map<T> {
127    fn from(value: HashMap<String, T>) -> Self {
128        Self {
129            map: value,
130            if_none: None,
131            r#else: None
132        }
133    }
134}
135
136impl<T> From<HashMap<Option<String>, T>> for Map<T> {
137    fn from(value: HashMap<Option<String>, T>) -> Self {
138        let mut ret = Self {
139            map: HashMap::with_capacity(value.len()),
140            if_none: None,
141            r#else: None
142        };
143
144        for (k, v) in value {
145            match k {
146                Some(k) => {ret.map.insert(k, v);},
147                None => ret.if_none = Some(Box::new(v))
148            }
149        }
150
151        ret
152    }
153}
154
155impl<T> FromIterator<(String, T)> for Map<T> {
156    fn from_iter<I: IntoIterator<Item = (String, T)>>(iter: I) -> Self {
157        Self {
158            map: iter.into_iter().collect(),
159            if_none: None,
160            r#else: None
161        }
162    }
163}
164
165impl<T> FromIterator<(Option<String>, T)> for Map<T> {
166    fn from_iter<I: IntoIterator<Item = (Option<String>, T)>>(iter: I) -> Self {
167        let iter = iter.into_iter();
168        let size_hint = iter.size_hint();
169        let mut ret = Self {
170            map: HashMap::with_capacity(size_hint.1.unwrap_or(size_hint.0)),
171            if_none: None,
172            r#else: None
173        };
174        for (k, v) in iter {
175            match k {
176                Some(k) => {ret.map.insert(k, v);},
177                None => ret.if_none = Some(Box::new(v))
178            }
179        }
180        ret
181    }
182}