Skip to content

Commit 1251c9a

Browse files
Initial commit
0 parents  commit 1251c9a

File tree

4 files changed

+443
-0
lines changed

4 files changed

+443
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
/Cargo.lock

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "fcidr"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]

src/cidr.rs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
use std::{
2+
fmt::{Debug, Display},
3+
net::Ipv4Addr,
4+
str::FromStr,
5+
};
6+
7+
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
8+
pub struct Cidr {
9+
network: Ipv4Addr,
10+
prefix: u8,
11+
}
12+
13+
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
14+
pub enum Error {
15+
CidrBoundsError(String),
16+
InvalidNetworkError(String),
17+
PrefixRangeError(String),
18+
ParseError(String),
19+
TypeCastError(String),
20+
ImpossibleError(String),
21+
}
22+
23+
impl Cidr {
24+
pub fn new(network: Ipv4Addr, prefix: u8) -> Result<Self, Error> {
25+
if prefix as u32 > u32::BITS {
26+
return Err(Error::PrefixRangeError(format!(
27+
"network prefix '{prefix}' must be 32 or less"
28+
)));
29+
}
30+
if network
31+
.octets()
32+
.iter()
33+
.skip((prefix / 8).into())
34+
.enumerate()
35+
.any(|(i, &o)| {
36+
let offset = prefix % 8;
37+
(if i > 0 { o } else { o << offset >> offset }) != 0
38+
})
39+
{
40+
return Err(Error::InvalidNetworkError(format!(
41+
"network address '{network}' must be clear after the first {prefix} bits"
42+
)));
43+
}
44+
Ok(Self { network, prefix })
45+
}
46+
47+
pub fn network(&self) -> Ipv4Addr {
48+
self.network
49+
}
50+
51+
pub fn prefix(&self) -> u8 {
52+
self.prefix
53+
}
54+
55+
pub fn first(&self) -> Ipv4Addr {
56+
self.network
57+
}
58+
59+
pub fn mid(&self) -> Ipv4Addr {
60+
if self.prefix as u32 == u32::BITS {
61+
self.network
62+
} else {
63+
(u32::from(self.network) | (1 << (u32::BITS - self.prefix as u32 - 1))).into()
64+
}
65+
}
66+
67+
pub fn last(&self) -> Ipv4Addr {
68+
let mut last = self.network.octets();
69+
let first_octet: usize = (self.prefix() / 8).into();
70+
for i in first_octet..last.len() {
71+
if i > first_octet {
72+
last[i] = u8::MAX
73+
} else {
74+
let offset = self.prefix % 8;
75+
last[i] |= u8::MAX << offset >> offset;
76+
}
77+
}
78+
Ipv4Addr::from(last)
79+
}
80+
81+
pub fn contains<T>(&self, net: T) -> Result<bool, Error>
82+
where
83+
T: Copy + Debug + TryInto<Cidr>,
84+
{
85+
let cidr: Cidr = net.try_into().map_err(|_| {
86+
Error::TypeCastError(format!("could not cast value '{:?}' to cidr", net))
87+
})?;
88+
Ok(cidr.first() >= self.first() && cidr.last() <= self.last())
89+
}
90+
91+
pub fn split(&self) -> Result<[Cidr; 2], Error> {
92+
let prefix = self.prefix + 1;
93+
Ok([
94+
Self::new(self.network, prefix)?,
95+
Self::new(self.mid(), prefix)?,
96+
])
97+
}
98+
}
99+
100+
impl Default for Cidr {
101+
fn default() -> Self {
102+
Self {
103+
network: Ipv4Addr::from(<[u8; 4]>::default()),
104+
prefix: Default::default(),
105+
}
106+
}
107+
}
108+
109+
impl Display for Cidr {
110+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
111+
write!(f, "{}/{}", self.network, self.prefix)
112+
}
113+
}
114+
115+
impl TryFrom<Ipv4Addr> for Cidr {
116+
type Error = Error;
117+
118+
fn try_from(value: Ipv4Addr) -> Result<Self, Self::Error> {
119+
Self::new(value, u32::BITS as u8)
120+
}
121+
}
122+
123+
impl FromStr for Cidr {
124+
type Err = Error;
125+
126+
fn from_str(s: &str) -> Result<Self, Self::Err> {
127+
if let Some((network, prefix)) = s.split_once('/') {
128+
Self::new(
129+
network
130+
.parse::<Ipv4Addr>()
131+
.map_err(|e| Error::ParseError(e.to_string()))?,
132+
prefix
133+
.parse::<u8>()
134+
.map_err(|e| Error::ParseError(e.to_string()))?,
135+
)
136+
} else {
137+
Err(Error::ParseError(
138+
"missing network prefix delimiter".to_owned(),
139+
))
140+
}
141+
}
142+
}
143+
144+
#[cfg(test)]
145+
mod tests {
146+
use super::*;
147+
148+
// #[test]
149+
// fn cidr_constructor() {
150+
// for prefix in 0..=32 {
151+
// println!("{}", Cidr::new(Ipv4Addr::new(0b10000000, 0, 0, 0), prefix).unwrap());
152+
// println!("{}", Cidr::new(Ipv4Addr::new(0xFF, 0xFF, 0xFF, 0xFF), prefix).unwrap());
153+
// }
154+
// }
155+
156+
// #[test]
157+
// fn cidr_first() {
158+
// let cidr: Cidr = "10.0.0.0/8".parse().unwrap();
159+
// println!("{} / {} : {} -> {}", cidr.network(), cidr.prefix(), cidr.first(), cidr.last());
160+
// let cidr: Cidr = "10.0.0.0/9".parse().unwrap();
161+
// println!("{} / {} : {} -> {}", cidr.network(), cidr.prefix(), cidr.first(), cidr.last());
162+
// let cidr: Cidr = "10.128.0.0/9".parse().unwrap();
163+
// println!("{} / {} : {} -> {}", cidr.network(), cidr.prefix(), cidr.first(), cidr.last());
164+
// let cidr: Cidr = "10.128.0.0/8".parse().unwrap();
165+
// println!("{} / {} : {} -> {}", cidr.network(), cidr.prefix(), cidr.first(), cidr.last());
166+
// }
167+
168+
#[test]
169+
fn it_works() {
170+
// let c: Cidr = "10.0.0.0/8".parse().unwrap();
171+
// let [l, r] = c.split().unwrap();
172+
// println!("{l}, {r}");
173+
// for i in 0..=32 {
174+
// println!("{} {}", i / 8, i % 8);
175+
// }
176+
// let o = 127_u8;
177+
// println!("{}", o == o >> 1 << 1);
178+
// println!("{}", "127.0.343.0".parse::<Ipv4Addr>().unwrap());
179+
// println!("{}", "127.0.343.0".parse::<Cidr>().unwrap());
180+
}
181+
}

0 commit comments

Comments
 (0)