1use serde::{Deserialize, Serialize};
6use std::fmt;
7
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct Asn(AsnData);
16
17#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18enum AsnData {
19 Unspecified,
20 Specified(u32),
21}
22
23impl Asn {
24 pub const fn from_static(value: u32) -> Self {
26 if value == 0 {
27 return Self(AsnData::Unspecified);
28 }
29 if !is_valid_asn_range(value) {
30 panic!("invalid ASN range")
31 }
32 Self(AsnData::Specified(value))
33 }
34 pub fn unspecified() -> Self {
38 Self(AsnData::Unspecified)
39 }
40
41 pub fn as_u32(&self) -> u32 {
43 match self.0 {
44 AsnData::Specified(n) => n,
45 AsnData::Unspecified => 0,
46 }
47 }
48
49 pub fn is_any(&self) -> bool {
51 self.0 == AsnData::Unspecified
52 }
53}
54
55const fn is_valid_asn_range(value: u32) -> bool {
56 (value >= 1 && value <= 23455)
57 || (value >= 23457 && value <= 64495)
58 || (value >= 131072 && value <= 4294967294)
59}
60
61impl TryFrom<u32> for Asn {
62 type Error = InvalidAsn;
63
64 fn try_from(value: u32) -> Result<Self, Self::Error> {
65 if value == 0 {
66 return Ok(Self(AsnData::Unspecified));
67 }
68 is_valid_asn_range(value)
69 .then_some(Self(AsnData::Specified(value)))
70 .ok_or(InvalidAsn)
71 }
72}
73
74impl TryFrom<&str> for Asn {
75 type Error = InvalidAsn;
76
77 fn try_from(value: &str) -> Result<Self, Self::Error> {
78 let value: u32 = value.parse().map_err(|_| InvalidAsn)?;
79 value.try_into()
80 }
81}
82
83impl TryFrom<String> for Asn {
84 type Error = InvalidAsn;
85
86 fn try_from(value: String) -> Result<Self, Self::Error> {
87 value.as_str().try_into()
88 }
89}
90
91impl TryFrom<&String> for Asn {
92 type Error = InvalidAsn;
93
94 fn try_from(value: &String) -> Result<Self, Self::Error> {
95 value.as_str().try_into()
96 }
97}
98
99impl TryFrom<&[u8]> for Asn {
100 type Error = InvalidAsn;
101
102 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
103 std::str::from_utf8(value)
104 .map_err(|_| InvalidAsn)?
105 .try_into()
106 }
107}
108
109impl fmt::Display for Asn {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 match self.0 {
112 AsnData::Specified(n) => write!(f, "AS{n}"),
113 AsnData::Unspecified => write!(f, "unspecified"),
114 }
115 }
116}
117
118#[cfg(feature = "venndb")]
119impl venndb::Any for Asn {
120 fn is_any(&self) -> bool {
121 Self::is_any(self)
122 }
123}
124
125impl Serialize for Asn {
126 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
127 where
128 S: serde::Serializer,
129 {
130 match self.0 {
131 AsnData::Unspecified => 0u32.serialize(serializer),
132 AsnData::Specified(u) => u.serialize(serializer),
133 }
134 }
135}
136
137impl<'de> Deserialize<'de> for Asn {
138 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139 where
140 D: serde::Deserializer<'de>,
141 {
142 let value = u32::deserialize(deserializer)?;
143 value
144 .try_into()
145 .map_err(|_| serde::de::Error::custom("invalid asn"))
146 }
147}
148
149rama_utils::macros::error::static_str_error! {
150 #[doc = "invalid ASN (e.g. within reserved space)"]
151 pub struct InvalidAsn;
152}