Skip to main content

proxmox_api/types/
multi.rs

1//! This module contains structures required to handle multi-fields, i.e.
2//! `link[n]`. In these fields, the name of the field is dependent on the number,
3//! so it requires some extra handling to get right.
4//!
5//! Additional data also needs extra handling in this case, which this module
6//! also provides.
7
8use std::{
9    collections::{BTreeMap, HashMap},
10    marker::PhantomData,
11};
12
13use serde::{Deserializer, Serialize, Serializer, de::DeserializeOwned, ser::SerializeMap};
14
15pub trait NumberedItems: Default {
16    type Item: DeserializeOwned + Serialize;
17    const PREFIX: &'static str;
18
19    fn key_matches(name: &str) -> bool {
20        Self::check_key(name).is_some()
21    }
22
23    fn check_key(name: &str) -> Option<u32> {
24        let (first_part, second_part) = name.split_once(Self::PREFIX)?;
25
26        if first_part.is_empty() {
27            let num = second_part.parse::<u32>().ok()?;
28            Some(num)
29        } else {
30            None
31        }
32    }
33
34    fn make_key(number: &u32) -> String {
35        format!("{}{}", Self::PREFIX, number)
36    }
37}
38
39pub trait Test {
40    fn test_fn() -> fn(&str) -> bool;
41}
42
43pub fn deserialize_additional_data<'de, T, O, D>(d: D) -> Result<HashMap<String, O>, D::Error>
44where
45    D: Deserializer<'de>,
46    T: Test,
47    O: serde::de::Deserialize<'de>,
48{
49    struct Visitor<O, T> {
50        _phantom: PhantomData<(O, T)>,
51    }
52
53    impl<O, T> Default for Visitor<O, T> {
54        fn default() -> Self {
55            Self {
56                _phantom: Default::default(),
57            }
58        }
59    }
60
61    impl<'de, O, T> serde::de::Visitor<'de> for Visitor<O, T>
62    where
63        O: serde::de::Deserialize<'de>,
64        T: Test,
65    {
66        type Value = HashMap<String, O>;
67
68        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
69            write!(f, "additional properties")
70        }
71
72        fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
73        where
74            A: serde::de::MapAccess<'de>,
75        {
76            let mut output = HashMap::new();
77
78            loop {
79                let (key, value) = match map.next_entry::<&str, O>() {
80                    Ok(Some(key)) => key,
81                    Ok(None) => break,
82                    Err(_) => continue,
83                };
84
85                if (T::test_fn())(key) {
86                    continue;
87                }
88
89                output.insert(key.to_string(), value);
90            }
91
92            Ok(output)
93        }
94    }
95
96    d.deserialize_map(Visitor::<O, T>::default())
97}
98
99macro_rules! define_multi_fns {
100    ($([$type:ident, $ser:ident, $des:ident]),*) => {
101        $(
102            pub fn $ser<V, S>(value: &$type<u32, V::Item>, s: S) -> Result<S::Ok, S::Error>
103            where
104                S: Serializer,
105                V: NumberedItems,
106            {
107                let mut map = s.serialize_map(Some(value.len()))?;
108
109                for (key, value) in value.iter() {
110                    map.serialize_entry(&V::make_key(key), value)?;
111                }
112
113                map.end()
114            }
115
116            pub fn $des<'de, V, D>(d: D) -> Result<$type<u32, V::Item>, D::Error>
117            where
118                V: NumberedItems,
119                D: Deserializer<'de>,
120            {
121                struct InternalVisitor<V, D> {
122                    _phantom: PhantomData<(D, V)>,
123                }
124
125                impl<V, D> Default for InternalVisitor<V, D> {
126                    fn default() -> Self {
127                        Self {
128                            _phantom: Default::default(),
129                        }
130                    }
131                }
132
133                impl<'de, V, D> serde::de::Visitor<'de> for InternalVisitor<V, D>
134                where
135                    V: NumberedItems,
136                    D: Deserializer<'de>,
137                {
138                    type Value = $type<u32, V::Item>;
139
140                    fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
141                        write!(f, "Multi-numbered items with prefix {}", V::PREFIX)
142                    }
143
144                    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
145                    where
146                        A: serde::de::MapAccess<'de>,
147                    {
148                        let mut output = $type::new();
149
150                        while let Some(key) = map.next_key::<&str>()? {
151                            let key = if let Some(idx) = V::check_key(key) {
152                                idx
153                            } else {
154                                continue;
155                            };
156
157                            let value = map.next_value::<V::Item>()?;
158                            output.insert(key, value);
159                        }
160
161                        Ok(output)
162                    }
163                }
164
165                d.deserialize_map(InternalVisitor::<V, D>::default())
166            }
167        )*
168    };
169}
170
171define_multi_fns!(
172    [HashMap, serialize_multi, deserialize_multi],
173    [BTreeMap, serialize_multi_btree, deserialize_multi_btree]
174);
175
176#[cfg(test)]
177mod test {
178    use std::collections::{BTreeMap, HashMap};
179
180    use serde::{Deserialize, Serialize};
181
182    use super::{NumberedItems, Test};
183
184    #[derive(Default)]
185    struct NumberedNames;
186
187    impl NumberedItems for NumberedNames {
188        type Item = String;
189        const PREFIX: &'static str = "name";
190    }
191
192    #[derive(Debug, Deserialize, Serialize, PartialEq)]
193    struct Names {
194        #[serde(
195            flatten,
196            deserialize_with = "super::deserialize_multi_btree::<'_, NumberedNames, _>",
197            serialize_with = "super::serialize_multi_btree::<NumberedNames, _>"
198        )]
199        names: BTreeMap<u32, String>,
200        #[serde(
201            flatten,
202            deserialize_with = "super::deserialize_additional_data::<'_, Names, _, _>"
203        )]
204        additional_data: HashMap<String, String>,
205    }
206
207    impl Test for Names {
208        fn test_fn() -> fn(&str) -> bool {
209            fn the_test(input: &str) -> bool {
210                ["names"].contains(&input) || NumberedNames::key_matches(input)
211            }
212
213            the_test
214        }
215    }
216
217    fn map_kv<K, V, NK, NV>((k, v): (K, V)) -> (NK, NV)
218    where
219        K: Into<NK>,
220        V: Into<NV>,
221    {
222        (k.into(), v.into())
223    }
224
225    #[test]
226    pub fn deserialize_multi() {
227        let text = r#"{ "name1": "value1", "name2": "value2", "name_4": 0, "name_3": false, "name_2": "additional" }"#;
228
229        let names: Names = serde_json::from_str(text).unwrap();
230        let expected = Names {
231            names: [(1u32, "value1"), (2, "value2")]
232                .into_iter()
233                .map(map_kv)
234                .collect(),
235            additional_data: [("name_2", "additional")].into_iter().map(map_kv).collect(),
236        };
237
238        assert_eq!(names, expected);
239    }
240
241    #[test]
242    fn serialize_mluti() {
243        let names = Names {
244            names: [(1u32, "value1"), (2, "value2")]
245                .into_iter()
246                .map(map_kv)
247                .collect(),
248            additional_data: [("name_2", "additional")].into_iter().map(map_kv).collect(),
249        };
250
251        let expected = r#"{"name1":"value1","name2":"value2","name_2":"additional"}"#;
252        let json = serde_json::to_string(&names).unwrap();
253        assert_eq!(json, expected);
254    }
255}