1use std::fmt::{Debug, Display, Formatter};
2use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3
4#[derive(PartialEq, Copy, Clone, Eq, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum NextHopAddress {
10 Ipv4(Ipv4Addr),
11 Ipv6(Ipv6Addr),
12 Ipv6LinkLocal(Ipv6Addr, Ipv6Addr),
13}
14
15impl NextHopAddress {
16 pub const fn is_link_local(&self) -> bool {
18 match self {
19 NextHopAddress::Ipv4(x) => x.is_link_local(),
20 NextHopAddress::Ipv6(x) => (x.segments()[0] & 0xffc0) == 0xfe80,
21 NextHopAddress::Ipv6LinkLocal(_, _) => true,
22 }
23 }
24
25 pub const fn addr(&self) -> IpAddr {
27 match self {
28 NextHopAddress::Ipv4(x) => IpAddr::V4(*x),
29 NextHopAddress::Ipv6(x) => IpAddr::V6(*x),
30 NextHopAddress::Ipv6LinkLocal(x, _) => IpAddr::V6(*x),
31 }
32 }
33}
34
35impl Debug for NextHopAddress {
37 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38 match self {
39 NextHopAddress::Ipv4(x) => write!(f, "{}", x),
40 NextHopAddress::Ipv6(x) => write!(f, "{}", x),
41 NextHopAddress::Ipv6LinkLocal(x, y) => write!(f, "Ipv6LinkLocal({}, {})", x, y),
42 }
43 }
44}
45
46impl Display for NextHopAddress {
47 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48 match self {
49 NextHopAddress::Ipv4(v) => write!(f, "{}", v),
50 NextHopAddress::Ipv6(v) => write!(f, "{}", v),
51 NextHopAddress::Ipv6LinkLocal(v, _) => write!(f, "{}", v),
52 }
53 }
54}
55
56impl From<IpAddr> for NextHopAddress {
57 fn from(value: IpAddr) -> Self {
58 match value {
59 IpAddr::V4(x) => NextHopAddress::Ipv4(x),
60 IpAddr::V6(x) => NextHopAddress::Ipv6(x),
61 }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
69
70 #[test]
71 fn test_next_hop_address_is_link_local() {
72 let ipv4_addr = Ipv4Addr::new(169, 254, 0, 1);
73 let ipv6_addr = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0);
74 let ipv6_link_local_addrs = (
75 Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 1),
76 Ipv6Addr::new(0xfe80, 0, 0, 2, 0, 0, 0, 1),
77 );
78
79 let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
80 let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
81 let next_hop_ipv6_link_local =
82 NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
83
84 assert!(next_hop_ipv4.is_link_local());
85 assert!(next_hop_ipv6.is_link_local());
86 assert!(next_hop_ipv6_link_local.is_link_local());
87 }
88
89 #[test]
90 fn test_next_hop_address_addr() {
91 let ipv4_addr = Ipv4Addr::new(192, 0, 2, 1);
92 let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
93 let ipv6_link_local_addrs = (
94 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
95 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
96 );
97
98 let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
99 let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
100 let next_hop_ipv6_link_local =
101 NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
102
103 assert_eq!(next_hop_ipv4.addr(), IpAddr::V4(ipv4_addr));
104 assert_eq!(next_hop_ipv6.addr(), IpAddr::V6(ipv6_addr));
105 assert_eq!(
106 next_hop_ipv6_link_local.addr(),
107 IpAddr::V6(ipv6_link_local_addrs.0)
108 );
109 }
110
111 #[test]
112 fn test_next_hop_address_from() {
113 let ipv4_addr = IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1));
114 let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
115
116 let next_hop_ipv4 = NextHopAddress::from(ipv4_addr);
117 let next_hop_ipv6 = NextHopAddress::from(ipv6_addr);
118
119 assert_eq!(next_hop_ipv4.addr(), ipv4_addr);
120 assert_eq!(next_hop_ipv6.addr(), ipv6_addr);
121 }
122
123 #[test]
124 fn test_debug_for_next_hop_address() {
125 let ipv4_addr = Ipv4Addr::new(192, 0, 2, 1);
126 let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
127 let ipv6_link_local_addrs = (
128 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
129 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
130 );
131
132 let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
133 let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
134 let next_hop_ipv6_link_local =
135 NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
136
137 assert_eq!(format!("{:?}", next_hop_ipv4), "192.0.2.1");
138 assert_eq!(format!("{:?}", next_hop_ipv6), "2001:db8::1");
139 assert_eq!(
140 format!("{:?}", next_hop_ipv6_link_local),
141 "Ipv6LinkLocal(fe80::, fe80::)"
142 );
143 }
144
145 #[test]
146 fn test_display_for_next_hop_address() {
147 let ipv4_addr = Ipv4Addr::new(192, 0, 2, 1);
148 let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
149 let ipv6_link_local_addrs = (
150 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
151 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
152 );
153
154 let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
155 let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
156 let next_hop_ipv6_link_local =
157 NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
158
159 assert_eq!(format!("{}", next_hop_ipv4), "192.0.2.1");
160 assert_eq!(format!("{}", next_hop_ipv6), "2001:db8::1");
161 assert_eq!(format!("{}", next_hop_ipv6_link_local), "fe80::");
162 }
163}