Skip to main content

renox_core/validation/
key_values.rs

1//! `KeyValues`: the pairs of the UI kit's `key_value` field.
2
3use serde::de::{self, MapAccess, SeqAccess, Visitor};
4use serde::ser::SerializeSeq;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7use super::value::{FieldValue, Inspected};
8use crate::db::DbValue;
9
10/// Pairs of text in the order they were entered, like headers or settings:
11/// what the UI kit's `key_value` field sends (`meta[0][key]`,
12/// `meta[0][value]`, …). Rows without a key are skipped, and a key entered
13/// twice keeps its last value.
14///
15/// It is stored (`db::Json<KeyValues>`) and serialized as a list of pairs,
16/// `[["Origin", "Aceh"], ["Roast", "Medium"]]`, so the order holds (a JSON
17/// object would lose it in PostgreSQL's `JSONB`), and reads back from that,
18/// or from an object; `key_value("meta", "Meta", value=item.meta)` shows
19/// what was saved.
20///
21/// ```
22/// # use renox::prelude::*;
23/// # use renox::KeyValues;
24/// #[derive(serde::Deserialize)]
25/// struct Settings {
26///     #[serde(default)]
27///     headers: KeyValues,
28/// }
29///
30/// impl Validate for Settings {
31///     fn rules(&self, v: &mut Validator) {
32///         v.field("headers", &self.headers).max(20);
33///     }
34/// }
35/// # fn demo(form: Settings) {
36/// for (name, value) in form.headers.iter() {
37///     println!("{name}: {value}");
38/// }
39/// assert_eq!(form.headers.get("Accept"), None);
40/// # }
41/// ```
42#[derive(Debug, Clone, Default, PartialEq, Eq)]
43pub struct KeyValues(Vec<(String, String)>);
44
45impl KeyValues {
46    /// No pairs.
47    pub fn new() -> Self {
48        Self::default()
49    }
50
51    /// The value of `key`, if there is one.
52    pub fn get(&self, key: &str) -> Option<&str> {
53        self.0
54            .iter()
55            .find(|(k, _)| k == key)
56            .map(|(_, v)| v.as_str())
57    }
58
59    /// Sets `key` to `value`, keeping its place if it was there.
60    pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) {
61        let (key, value) = (key.into(), value.into());
62        match self.0.iter_mut().find(|(k, _)| *k == key) {
63            Some(pair) => pair.1 = value,
64            None => self.0.push((key, value)),
65        }
66    }
67
68    /// The pairs, in order.
69    pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
70        self.0.iter().map(|(k, v)| (k.as_str(), v.as_str()))
71    }
72
73    /// How many pairs there are.
74    pub fn len(&self) -> usize {
75        self.0.len()
76    }
77
78    /// Whether there are none.
79    pub fn is_empty(&self) -> bool {
80        self.0.is_empty()
81    }
82
83    /// The pairs as a `Vec`.
84    pub fn into_vec(self) -> Vec<(String, String)> {
85        self.0
86    }
87}
88
89impl<K: Into<String>, V: Into<String>> FromIterator<(K, V)> for KeyValues {
90    fn from_iter<I: IntoIterator<Item = (K, V)>>(pairs: I) -> Self {
91        let mut all = KeyValues::new();
92        for (key, value) in pairs {
93            all.insert(key, value);
94        }
95        all
96    }
97}
98
99impl Serialize for KeyValues {
100    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
101        let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
102        for pair in &self.0 {
103            seq.serialize_element(pair)?;
104        }
105        seq.end()
106    }
107}
108
109/// A row of the form, `{key, value}` (either may be missing or empty), or a
110/// stored pair, `[key, value]`.
111#[derive(Deserialize)]
112#[serde(untagged)]
113enum Row {
114    Form {
115        #[serde(default)]
116        key: Option<String>,
117        #[serde(default)]
118        value: Option<String>,
119    },
120    Pair(String, String),
121}
122
123impl<'de> Deserialize<'de> for KeyValues {
124    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
125        struct Pairs;
126
127        impl<'de> Visitor<'de> for Pairs {
128            type Value = KeyValues;
129
130            fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
131                f.write_str("rows of key and value, or an object")
132            }
133
134            // The form's `{key, value}` rows, or the stored `[key, value]` pairs.
135            fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<KeyValues, A::Error> {
136                let mut all = KeyValues::new();
137                while let Some(row) = seq.next_element::<Row>()? {
138                    let (key, value) = match row {
139                        Row::Form { key, value } => {
140                            (key.unwrap_or_default(), value.unwrap_or_default())
141                        }
142                        Row::Pair(key, value) => (key, value),
143                    };
144                    let key = key.trim();
145                    if !key.is_empty() {
146                        all.insert(key, value);
147                    }
148                }
149                Ok(all)
150            }
151
152            // An object (written by hand, or another app).
153            fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<KeyValues, A::Error> {
154                let mut all = KeyValues::new();
155                while let Some((key, value)) = map.next_entry::<String, String>()? {
156                    all.insert(key, value);
157                }
158                Ok(all)
159            }
160
161            // An empty field.
162            fn visit_str<E: de::Error>(self, value: &str) -> Result<KeyValues, E> {
163                if value.trim().is_empty() {
164                    Ok(KeyValues::new())
165                } else {
166                    Err(E::invalid_type(de::Unexpected::Str(value), &self))
167                }
168            }
169
170            fn visit_unit<E: de::Error>(self) -> Result<KeyValues, E> {
171                Ok(KeyValues::new())
172            }
173        }
174
175        deserializer.deserialize_any(Pairs)
176    }
177}
178
179impl FieldValue for KeyValues {
180    fn inspect(&self) -> Inspected {
181        if self.is_empty() {
182            Inspected::Missing
183        } else {
184            Inspected::Items(self.len())
185        }
186    }
187
188    fn db_value(&self) -> DbValue {
189        DbValue::Null
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196    use crate::validation::nested::{Node, deserialize};
197
198    #[test]
199    fn reads_form_rows_and_saved_objects() {
200        #[derive(Deserialize)]
201        struct Form {
202            meta: KeyValues,
203        }
204        let pairs: Vec<(String, String)> = [
205            ("meta[0][key]", "Color"),
206            ("meta[0][value]", "Red"),
207            ("meta[1][key]", ""),
208            ("meta[1][value]", "ignored"),
209            ("meta[2][key]", " Size "),
210            ("meta[2][value]", "L"),
211            ("meta[3][key]", "Color"),
212            ("meta[3][value]", "Blue"),
213        ]
214        .iter()
215        .map(|(k, v)| ((*k).to_owned(), (*v).to_owned()))
216        .collect();
217        let form: Form = deserialize(Node::build(&pairs)).unwrap();
218        assert_eq!(
219            form.meta.iter().collect::<Vec<_>>(),
220            [("Color", "Blue"), ("Size", "L")]
221        );
222        let json = serde_json::to_string(&form.meta).unwrap();
223        assert_eq!(json, r#"[["Color","Blue"],["Size","L"]]"#);
224        let back: KeyValues = serde_json::from_str(&json).unwrap();
225        assert_eq!(back, form.meta);
226        assert_eq!(back.get("Size"), Some("L"));
227        let object: KeyValues = serde_json::from_str(r#"{"Size":"L","Color":"Blue"}"#).unwrap();
228        assert_eq!(
229            object.iter().collect::<Vec<_>>(),
230            [("Size", "L"), ("Color", "Blue")]
231        );
232        let empty: KeyValues = serde_json::from_str(r#""""#).unwrap();
233        assert!(empty.is_empty());
234    }
235
236    #[test]
237    fn text_that_isnt_empty_and_null_are_handled() {
238        // Some text where rows were expected is refused, naming what's wanted.
239        let err = serde_json::from_str::<KeyValues>(r#""colour=red""#).unwrap_err();
240        assert!(
241            err.to_string()
242                .contains("expected rows of key and value, or an object"),
243            "{err}"
244        );
245        // An empty text box and `null` are no pairs.
246        assert!(
247            serde_json::from_str::<KeyValues>(r#""  ""#)
248                .unwrap()
249                .is_empty()
250        );
251        assert!(
252            serde_json::from_str::<KeyValues>("null")
253                .unwrap()
254                .is_empty()
255        );
256        let pairs: KeyValues =
257            serde_json::from_str(r#"[["a", "1"], {"key": " b ", "value": "2"}, {"key": ""}]"#)
258                .unwrap();
259        assert_eq!(
260            pairs.into_vec(),
261            [
262                ("a".to_owned(), "1".to_owned()),
263                ("b".to_owned(), "2".to_owned())
264            ]
265        );
266    }
267}