bgpkit_parser/models/network/
prefix.rs1use crate::models::BgpModelsError;
2#[cfg(feature = "parser")]
3use bytes::{BufMut, Bytes, BytesMut};
4use ipnet::IpNet;
5use std::fmt::{Debug, Display, Formatter};
6use std::str::FromStr;
7
8#[derive(PartialEq, Eq, Clone, Copy, Hash)]
10pub struct NetworkPrefix {
11 pub prefix: IpNet,
12 pub path_id: Option<u32>,
13}
14
15impl Debug for NetworkPrefix {
17 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
18 match self.path_id {
19 Some(path_id) => write!(f, "{}#{}", self.prefix, path_id),
20 None => write!(f, "{}", self.prefix),
21 }
22 }
23}
24
25impl FromStr for NetworkPrefix {
26 type Err = BgpModelsError;
27
28 fn from_str(s: &str) -> Result<Self, Self::Err> {
29 let prefix = IpNet::from_str(s)?;
30 Ok(NetworkPrefix {
31 prefix,
32 path_id: None,
33 })
34 }
35}
36
37impl NetworkPrefix {
38 pub fn new(prefix: IpNet, path_id: Option<u32>) -> NetworkPrefix {
39 NetworkPrefix { prefix, path_id }
40 }
41
42 #[cfg(feature = "parser")]
43 pub fn encode(&self) -> Bytes {
67 let mut bytes = BytesMut::new();
68
69 if let Some(path_id) = self.path_id {
71 bytes.put_u32(path_id);
73 }
74
75 let bit_len = self.prefix.prefix_len();
78 let byte_len = bit_len.div_ceil(8) as usize;
79 bytes.put_u8(bit_len);
80
81 match self.prefix {
82 IpNet::V4(prefix) => {
83 bytes.put_slice(&prefix.addr().octets()[0..byte_len]);
84 }
85 IpNet::V6(prefix) => {
86 bytes.put_slice(&prefix.addr().octets()[0..byte_len]);
87 }
88 };
89 bytes.freeze()
90 }
91}
92
93impl Display for NetworkPrefix {
94 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
95 write!(f, "{}", self.prefix)
96 }
97}
98
99#[cfg(feature = "serde")]
100mod serde_impl {
101 use super::*;
102 use serde::{Deserialize, Deserializer, Serialize, Serializer};
103
104 #[derive(Serialize, Deserialize)]
105 #[serde(untagged, deny_unknown_fields)]
106 enum SerdeNetworkPrefixRepr {
107 PlainPrefix(IpNet),
108 WithPathId { prefix: IpNet, path_id: u32 },
109 }
110
111 impl Serialize for NetworkPrefix {
112 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
113 where
114 S: Serializer,
115 {
116 match self.path_id {
117 Some(path_id) => SerdeNetworkPrefixRepr::WithPathId {
118 prefix: self.prefix,
119 path_id,
120 }
121 .serialize(serializer),
122 None => self.prefix.serialize(serializer),
123 }
124 }
125 }
126
127 impl<'de> Deserialize<'de> for NetworkPrefix {
128 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
129 where
130 D: Deserializer<'de>,
131 {
132 match SerdeNetworkPrefixRepr::deserialize(deserializer)? {
133 SerdeNetworkPrefixRepr::PlainPrefix(prefix) => Ok(NetworkPrefix {
134 prefix,
135 path_id: None,
136 }),
137 SerdeNetworkPrefixRepr::WithPathId { prefix, path_id } => Ok(NetworkPrefix {
138 prefix,
139 path_id: Some(path_id),
140 }),
141 }
142 }
143 }
144}
145
146#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_fromstr() {
153 let prefix_str = "192.168.0.0/24";
154 let network_prefix = NetworkPrefix::from_str(prefix_str).unwrap();
155 assert_eq!(
156 network_prefix.prefix,
157 IpNet::from_str("192.168.0.0/24").unwrap()
158 );
159 assert_eq!(network_prefix.path_id, None);
160 }
161
162 #[test]
163 #[cfg(feature = "parser")]
164 fn test_encode() {
165 let prefix = IpNet::from_str("192.168.0.0/24").unwrap();
166 let network_prefix = NetworkPrefix::new(prefix, Some(1));
167 let _encoded = network_prefix.encode();
168 }
169
170 #[test]
171 fn test_display() {
172 let prefix = IpNet::from_str("192.168.0.0/24").unwrap();
173 let network_prefix = NetworkPrefix::new(prefix, Some(1));
174 assert_eq!(network_prefix.to_string(), "192.168.0.0/24");
175 }
176
177 #[test]
178 #[cfg(feature = "serde")]
179 fn test_serialization() {
180 let prefix = IpNet::from_str("192.168.0.0/24").unwrap();
181 let network_prefix = NetworkPrefix::new(prefix, Some(1));
182 let serialized = serde_json::to_string(&network_prefix).unwrap();
183 assert_eq!(serialized, "{\"prefix\":\"192.168.0.0/24\",\"path_id\":1}");
184 }
185
186 #[test]
187 #[cfg(feature = "serde")]
188 fn test_deserialization() {
189 let serialized = "{\"prefix\":\"192.168.0.0/24\",\"path_id\":1}";
190 let deserialized: NetworkPrefix = serde_json::from_str(serialized).unwrap();
191 assert_eq!(
192 deserialized.prefix,
193 IpNet::from_str("192.168.0.0/24").unwrap()
194 );
195 assert_eq!(deserialized.path_id, Some(1));
196 }
197
198 #[test]
199 #[cfg(feature = "serde")]
200 fn test_binary_serialization_with_path_id() {
201 let prefix = IpNet::from_str("192.168.0.0/24").unwrap();
202 let network_prefix = NetworkPrefix::new(prefix, Some(42));
203 let serialized = serde_json::to_vec(&network_prefix).unwrap();
205 let deserialized: NetworkPrefix = serde_json::from_slice(&serialized).unwrap();
206 assert_eq!(deserialized.prefix, prefix);
207 assert_eq!(deserialized.path_id, Some(42));
208 }
209
210 #[test]
211 fn test_debug() {
212 let prefix = IpNet::from_str("192.168.0.0/24").unwrap();
213 let network_prefix = NetworkPrefix::new(prefix, Some(1));
214 assert_eq!(format!("{network_prefix:?}"), "192.168.0.0/24#1");
215 }
216}