use crate::{Buf, BufMut, BufResult, Codec, Cursor};
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Address(pub [u8; 4]);
impl Address {
pub const UNSPECIFIED: Self = Self([0, 0, 0, 0]);
pub const BROADCAST: Self = Self([255, 255, 0, 0]);
pub const LOOPBACK: Self = Self([127, 0, 0, 1]);
pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Self {
Self([a, b, c, d])
}
pub const fn from_octets(octets: [u8; 4]) -> Self {
Self(octets)
}
pub const fn octets(self) -> [u8; 4] {
self.0
}
pub const fn from_bits(bits: u32) -> Self {
Self(bits.to_be_bytes())
}
pub const fn to_bits(self) -> u32 {
u32::from_be_bytes(self.0)
}
pub const fn is_private(self) -> bool {
self.0[0] == 10
|| (self.0[0] == 172 && (self.0[1] & 0xF0) == 16)
|| (self.0[0] == 192 && self.0[1] == 168)
}
pub const fn is_link_local(self) -> bool {
self.0[0] == 169 && self.0[1] == 254
}
pub const fn is_multicast(self) -> bool {
(self.0[0] & 0xF0) == 224
}
pub const fn is_documentation(self) -> bool {
matches!(
(self.0[0], self.0[1], self.0[2]),
(192, 0, 2) | (198, 51, 100) | (203, 0, 113)
)
}
}
#[cfg(feature = "std")]
impl From<std::net::Ipv4Addr> for Address {
fn from(addr: std::net::Ipv4Addr) -> Self {
Self(addr.octets())
}
}
#[cfg(feature = "std")]
impl From<Address> for std::net::Ipv4Addr {
fn from(addr: Address) -> Self {
std::net::Ipv4Addr::from(addr.0)
}
}
impl From<[u8; 4]> for Address {
fn from(octets: [u8; 4]) -> Self {
Self(octets)
}
}
impl From<Address> for [u8; 4] {
fn from(addr: Address) -> Self {
addr.0
}
}
impl From<u32> for Address {
fn from(value: u32) -> Self {
Self(value.to_be_bytes())
}
}
impl From<Address> for u32 {
fn from(addr: Address) -> Self {
addr.to_bits()
}
}
impl Codec for Address {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
self.0.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
Ok(Self(reader.read_array::<4>()?))
}
}
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}.{}", self.0[0], self.0[1], self.0[2], self.0[3])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn address_properties() {
assert!(Address::new(10, 0, 0, 1).is_private());
assert!(Address::new(192, 168, 1, 1).is_private());
assert!(Address::new(169, 254, 1, 1).is_link_local());
assert!(Address::new(224, 0, 0, 1).is_multicast());
assert!(Address::new(192, 0, 2, 1).is_documentation());
}
}