subnet_garden_core/
lib.rs

1// Copyright 2023-2024 The Milton Hirsch Institute, B.V.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::errors::{AllocateError, CreateError, DeleteError, RenameError};
5use cidr::IpCidr;
6use serde::de;
7use serde::ser::SerializeStruct;
8use std::str::FromStr;
9
10pub mod errors;
11pub mod pool;
12mod subspace;
13mod util;
14
15pub type CreateResult<T> = Result<T, CreateError>;
16
17pub type DeleteResult<T> = Result<T, DeleteError>;
18
19pub type AllocateResult<T> = Result<T, AllocateError>;
20
21pub type RenameResult<T> = Result<T, RenameError>;
22
23pub type Bits = u8;
24
25#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Clone)]
26pub struct CidrRecord {
27    pub cidr: IpCidr,
28    pub name: Option<String>,
29}
30
31impl CidrRecord {
32    pub(crate) fn new(cidr: IpCidr, name: Option<&str>) -> Self {
33        CidrRecord {
34            cidr,
35            name: name.map(|name| name.to_string()),
36        }
37    }
38}
39
40impl serde::Serialize for CidrRecord {
41    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
42    where
43        S: serde::Serializer,
44    {
45        let mut structure = serializer.serialize_struct("CidrRecord", 2)?;
46        structure.serialize_field("cidr", &self.cidr.to_string())?;
47        if let Some(name) = &self.name {
48            structure.serialize_field("name", name)?;
49        }
50        structure.end()
51    }
52}
53
54impl<'s> serde::Deserialize<'s> for CidrRecord {
55    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
56    where
57        D: serde::Deserializer<'s>,
58    {
59        #[derive(serde::Deserialize)]
60        #[serde(field_identifier, rename_all = "lowercase")]
61        enum Field {
62            Cidr,
63            Name,
64        }
65        struct CidrRecordVisitor;
66        impl<'d> de::Visitor<'d> for CidrRecordVisitor {
67            type Value = CidrRecord;
68
69            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
70                formatter.write_str("struct CidrRecord")
71            }
72
73            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
74            where
75                A: de::SeqAccess<'d>,
76            {
77                let cidr_str = seq
78                    .next_element::<&str>()?
79                    .ok_or_else(|| de::Error::missing_field("cidr"))?;
80                let cidr = match IpCidr::from_str(cidr_str) {
81                    Ok(cidr) => cidr,
82                    Err(err) => return Err(serde::de::Error::custom(err)),
83                };
84
85                let name = seq
86                    .next_element::<Option<&str>>()?
87                    .ok_or_else(|| serde::de::Error::missing_field("name"))?;
88
89                Ok(CidrRecord::new(cidr, name))
90            }
91            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
92            where
93                A: de::MapAccess<'d>,
94            {
95                let mut cidr: Option<IpCidr> = None;
96                let mut name: Option<String> = None;
97                while let Some(key) = map.next_key()? {
98                    match key {
99                        Field::Cidr => {
100                            if cidr.is_some() {
101                                return Err(de::Error::duplicate_field("cidr"));
102                            }
103                            cidr = match map.next_value::<String>() {
104                                Ok(cidr) => match IpCidr::from_str(cidr.as_str()) {
105                                    Ok(cidr) => Some(cidr),
106                                    Err(err) => return Err(de::Error::custom(err)),
107                                },
108                                Err(err) => return Err(err),
109                            };
110                        }
111                        Field::Name => {
112                            if name.is_some() {
113                                return Err(de::Error::duplicate_field("name"));
114                            }
115                            name = Some(map.next_value()?);
116                        }
117                    }
118                }
119                let cidr = match cidr {
120                    Some(cidr) => cidr,
121                    None => return Err(de::Error::missing_field("cidr")),
122                };
123                Ok(CidrRecord::new(cidr, name.as_deref()))
124            }
125        }
126
127        deserializer.deserialize_struct("CidrRecord", &["cidr", "name"], CidrRecordVisitor)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134    use cidr::{Ipv4Cidr, Ipv6Cidr};
135    use std::net::{Ipv4Addr, Ipv6Addr};
136
137    pub static TEST_CIDR4: IpCidr =
138        IpCidr::V4(match Ipv4Cidr::new(Ipv4Addr::new(10, 20, 0, 0), 16) {
139            Ok(cidr) => cidr,
140            _ => panic!("Failed to create test v4 cidr"),
141        });
142
143    pub static TEST_CIDR6: IpCidr = IpCidr::V6(
144        match Ipv6Cidr::new(Ipv6Addr::new(1, 2, 3, 4, 10, 20, 0, 0), 112) {
145            Ok(cidr) => cidr,
146            _ => panic!("Failed to create test v6 cidr"),
147        },
148    );
149    mod cidr_record {
150        use super::*;
151        use serde_test::{assert_de_tokens_error, assert_tokens};
152        use std::str::FromStr;
153
154        #[test]
155        fn new() {
156            let cidr = IpCidr::from_str("10.20.30.0/24").unwrap();
157            let name = Some("foo");
158            let record = super::super::CidrRecord::new(cidr, name);
159            assert_eq!(record.cidr, cidr);
160            assert_eq!(record.name.as_deref(), name);
161        }
162
163        #[test]
164        fn serialize_named() {
165            let cidr = IpCidr::from_str("10.20.30.0/24").unwrap();
166            let record = CidrRecord::new(cidr, Some("a-record"));
167            assert_tokens(
168                &record,
169                &[
170                    serde_test::Token::Struct {
171                        name: "CidrRecord",
172                        len: 2,
173                    },
174                    serde_test::Token::Str("cidr"),
175                    serde_test::Token::Str("10.20.30.0/24"),
176                    serde_test::Token::Str("name"),
177                    serde_test::Token::Str("a-record"),
178                    serde_test::Token::StructEnd,
179                ],
180            );
181        }
182
183        #[test]
184        fn serialize_unnamed() {
185            let cidr = IpCidr::from_str("10.20.30.0/24").unwrap();
186            let record = CidrRecord::new(cidr, None);
187            assert_tokens(
188                &record,
189                &[
190                    serde_test::Token::Struct {
191                        name: "CidrRecord",
192                        len: 2,
193                    },
194                    serde_test::Token::Str("cidr"),
195                    serde_test::Token::Str("10.20.30.0/24"),
196                    serde_test::Token::StructEnd,
197                ],
198            );
199        }
200
201        #[test]
202        fn deserialize_as_sequence() {
203            assert_de_tokens_error::<CidrRecord>(
204                &[
205                    serde_test::Token::Struct {
206                        name: "CidrRecord",
207                        len: 2,
208                    },
209                    serde_test::Token::Str("cidr"),
210                    serde_test::Token::Str("invalid"),
211                    serde_test::Token::StructEnd,
212                ],
213                "couldn't parse address in network: invalid IP address syntax",
214            );
215        }
216    }
217}