Skip to main content

lowram_ipsum/
lib.rs

1#![no_std]
2
3use core::{
4    array,
5    net::{IpAddr, Ipv4Addr, Ipv6Addr},
6    ops::Add,
7};
8
9/// Wrapper for adding `IpAddr`, `Ipv4Addr` and `Ipv6Addr`.
10/// We love the orphan rule around here.
11///
12/// # Example
13///
14/// ```
15/// let sum: Ipv4addr = Ipsum(Ipv4Addr::new(1, 2, 3, 4)) + Ipsum(Ipv4Addr::new(1, 2, 3, 255));
16/// ```
17pub struct Ipsum<T>(pub T);
18
19impl Add for Ipsum<Ipv4Addr> {
20    type Output = Ipv4Addr;
21
22    fn add(self, rhs: Self) -> Self::Output {
23        let inner = self.0;
24        let rhs = rhs.0;
25        let mut it = inner
26            .octets()
27            .into_iter()
28            .zip(rhs.octets().into_iter())
29            .map(|(o1, o2)| o1.saturating_add(o2));
30        let res: [u8; 4] = array::from_fn(|_| it.next().unwrap());
31        res.into()
32    }
33}
34
35impl Add for Ipsum<Ipv6Addr> {
36    type Output = Ipv6Addr;
37
38    fn add(self, rhs: Self) -> Self::Output {
39        let inner = self.0;
40        let rhs = rhs.0;
41        let mut it = inner
42            .octets()
43            .into_iter()
44            .zip(rhs.octets().into_iter())
45            .map(|(o1, o2)| o1.saturating_add(o2));
46        let res: [u8; 16] = array::from_fn(|_| it.next().unwrap());
47        res.into()
48    }
49}
50
51impl Add for Ipsum<IpAddr> {
52    type Output = IpAddr;
53
54    fn add(self, rhs: Self) -> Self::Output {
55        match (self.0, rhs.0) {
56            (IpAddr::V4(lhs), IpAddr::V4(rhs)) => IpAddr::V4(Ipsum(lhs) + Ipsum(rhs)),
57            (IpAddr::V4(lhs), IpAddr::V6(rhs)) => {
58                IpAddr::V4(Ipsum(lhs) + Ipsum(rhs.to_ipv4().unwrap()))
59            }
60            (IpAddr::V6(lhs), IpAddr::V4(rhs)) => {
61                IpAddr::V4(Ipsum(lhs.to_ipv4().unwrap()) + Ipsum(rhs))
62            }
63            (IpAddr::V6(lhs), IpAddr::V6(rhs)) => IpAddr::V6(Ipsum(lhs) + Ipsum(rhs)),
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use core::u16;
71
72    use super::*;
73
74    #[test]
75    fn v4() {
76        let result = Ipsum(Ipv4Addr::new(1, 2, 3, 4)) + Ipsum(Ipv4Addr::new(1, 2, 3, 255));
77        assert_eq!(result, Ipv4Addr::new(2, 4, 6, 255));
78    }
79
80    #[test]
81    fn v6() {
82        let result = Ipsum(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0))
83            + Ipsum(Ipv6Addr::new(0x2001, 0xdb8, 1, 2, 3, 4, 5, u16::MAX));
84        let parts1 = 0x2001u16.to_be_bytes();
85        let parts2 = 0xdb8u16.to_be_bytes();
86        assert_eq!(
87            result,
88            Ipv6Addr::new(
89                u16::from_be_bytes([parts1[0].saturating_mul(2), parts1[1].saturating_mul(2)]),
90                u16::from_be_bytes([parts2[0].saturating_mul(2), parts2[1].saturating_mul(2)]),
91                1,
92                2,
93                3,
94                4,
95                5,
96                u16::MAX
97            )
98        );
99    }
100
101    #[test]
102    fn mixed() {
103        let result = Ipsum(IpAddr::from([127, 0, 0, 1]))
104            + Ipsum(Ipv4Addr::new(192, 168, 2, 4).to_ipv6_mapped().into());
105
106        assert_eq!(result, Ipv4Addr::new(127u8.saturating_add(192), 168, 2, 5));
107    }
108
109    #[test]
110    #[should_panic]
111    fn panics() {
112        let _kbye = Ipsum(IpAddr::from([127, 0, 0, 1]))
113            + Ipsum(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).into());
114    }
115}