externaldns_webhook/
changes.rs

1use crate::endpoint::Endpoint;
2use serde::{Deserialize, Serialize};
3use serde_with::{DefaultOnNull, serde_as};
4
5/// Pair with direction
6#[serde_as]
7#[derive(Serialize, Deserialize, Debug, PartialEq)]
8pub struct FromTo<T> {
9    pub from: T,
10    pub to: T,
11}
12
13/// Data structure posted from ExternalDNS
14/// The data represent the changes that ExternalDNS wants to make
15/// It is not certain that all fields would be filled in one request.
16/// Could be an Enum.
17#[serde_as]
18#[derive(Serialize, Deserialize, Debug, PartialEq)]
19#[serde(rename_all = "PascalCase")]
20pub struct Changes {
21    // Funny enough, when removing records, this field is `null`,
22    // instead of `[]` as used in other fields.
23    #[serde_as(deserialize_as = "DefaultOnNull")]
24    pub create: Vec<Endpoint>,
25    #[serde(flatten, with = "serde_fromto")]
26    pub update: Vec<FromTo<Endpoint>>,
27    pub delete: Vec<Endpoint>,
28}
29
30mod serde_fromto {
31    use super::FromTo;
32    use serde::de::Error;
33    use serde::{Deserialize, Deserializer, Serialize, Serializer};
34
35    #[derive(Serialize, Deserialize)]
36    struct FromTos<T> {
37        #[serde(rename = "UpdateOld")]
38        old: Vec<T>,
39        #[serde(rename = "UpdateNew")]
40        new: Vec<T>,
41    }
42
43    pub fn serialize<S, T>(fts: &Vec<FromTo<T>>, serializer: S) -> Result<S::Ok, S::Error>
44    where
45        S: Serializer,
46        T: Serialize + Clone,
47    {
48        let mut out = FromTos {
49            old: vec![],
50            new: vec![],
51        };
52        for ft in fts {
53            out.old.push(ft.from.clone());
54            out.new.push(ft.to.clone());
55        }
56
57        out.serialize(serializer)
58    }
59
60    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Vec<FromTo<T>>, D::Error>
61    where
62        D: Deserializer<'de>,
63        T: Deserialize<'de>,
64    {
65        let the_in = FromTos::deserialize(deserializer)?;
66        if the_in.old.len() == the_in.new.len() {
67            let ret: Vec<FromTo<_>> = std::iter::zip(the_in.old, the_in.new)
68                .map(|(from, to)| FromTo { from, to })
69                .collect();
70            Ok(ret)
71        } else {
72            Err(D::Error::custom(
73                "The count of old and new data are not the same",
74            ))
75        }
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn it_works() {
85        let changes = Changes {
86            update: vec![],
87            delete: vec![],
88            create: vec![],
89        };
90        let json: Result<Changes, _> =
91            serde_json::from_str(r##"{"Create":null,"UpdateOld":[],"UpdateNew":[],"Delete":[]}"##);
92        assert_eq!(json.unwrap(), changes);
93    }
94}