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