rama_net/
asn.rs

1//! autonomous system number (ASN)
2//!
3//! See [`Asn`] and its methods for more information.
4
5use serde::{Deserialize, Serialize};
6use std::fmt;
7
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9/// autonomous system number (ASN).
10///
11/// Within Rama this has little use as we do facilitate or drive BGP routing.
12/// It is however defined to allow interaction with services that do interact
13/// with this layer, such as proxy gateway services, especially one
14/// of residential type.
15pub 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    /// Create a valid ASN from a static number, validated at compile time.
25    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    /// Internally makes use of a value that's invalid within ASN,
35    /// but that be used to identify an AS with an unspecified number,
36    /// or a router that can route to the AS of a given ASN.
37    pub fn unspecified() -> Self {
38        Self(AsnData::Unspecified)
39    }
40
41    /// Return [`Asn`] as u32
42    pub fn as_u32(&self) -> u32 {
43        match self.0 {
44            AsnData::Specified(n) => n,
45            AsnData::Unspecified => 0,
46        }
47    }
48
49    /// Returns `true` if this value is considered to be "any" value.
50    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}