route_verification_ir/
set.rs

1use lazy_regex::regex_captures;
2
3use super::*;
4
5/// <https://www.rfc-editor.org/rfc/rfc2622#section-5.1>
6#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
7pub struct AsSet {
8    pub body: String,
9    /// AS numbers; should be kept sorted.
10    pub members: Vec<u32>,
11    pub set_members: Vec<String>,
12    pub is_any: bool,
13}
14
15impl AsSet {
16    pub fn new(mut body: String, mut members: Vec<u32>, mut set_members: Vec<String>) -> Self {
17        body.shrink_to_fit();
18        members.shrink_to_fit();
19        members.sort_unstable();
20        set_members.shrink_to_fit();
21        Self {
22            body,
23            members,
24            set_members,
25            is_any: false,
26        }
27    }
28
29    pub fn new_any(mut body: String) -> Self {
30        body.shrink_to_fit();
31        Self {
32            body,
33            members: vec![],
34            set_members: vec![],
35            is_any: true,
36        }
37    }
38}
39
40pub fn is_route_set_name(attr: &str) -> bool {
41    regex!(formatcp!("^{}$", ROUTE_SET)).is_match(attr)
42}
43
44/// > The attributes of the route-set class are shown in Figure 12.  The
45/// > route-set attribute defines the name of the set.  It is an RPSL name
46/// > that starts with "rs-".  The members attribute lists the members of
47/// > the set.  The members attribute is a list of address prefixes or
48/// > other route-set names.  Note that, the route-set class is a set of
49/// > route prefixes, not of RPSL route objects.
50///
51/// <https://www.rfc-editor.org/rfc/rfc2622#section-5.2>
52#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
53pub struct RouteSet {
54    pub body: String,
55    /// List of `<address-prefix-range>` or `<route-set-name>` or
56    /// `<route-set-name><range-operator>`.
57    pub members: Vec<RouteSetMember>,
58}
59
60#[derive(Clone, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
61pub enum RouteSetMember {
62    /// `<address-prefix-range>`
63    RSRange(AddrPfxRange),
64    /// `<route-set-name><range-operator>`
65    NameOp(String, RangeOperator),
66}
67
68impl std::fmt::Debug for RouteSetMember {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        use RouteSetMember::*;
71        match self {
72            RSRange(arg0) => f.debug_tuple("RSRange").field(arg0).finish(),
73            NameOp(arg0, arg1) => {
74                let mut r = f.debug_tuple("NameOp");
75                r.field(arg0);
76                if *arg1 != RangeOperator::NoOp {
77                    r.field(arg1);
78                }
79                r.finish()
80            }
81        }
82    }
83}
84
85impl From<String> for RouteSetMember {
86    fn from(value: String) -> Self {
87        if let Ok(range) = value.parse() {
88            Self::RSRange(range)
89        } else if let Ok((name, op)) = try_parse_name_operator(&value) {
90            Self::NameOp(name.into(), op)
91        } else {
92            Self::NameOp(value, RangeOperator::NoOp)
93        }
94    }
95}
96
97pub fn try_parse_name_operator(s: &str) -> Result<(&str, RangeOperator)> {
98    let (_, name, operator) =
99        get_name_operator(s).context(format!("{s} is not in valid NameOp form"))?;
100    let op = operator.parse().context(format!("parsing {s} as NameOp"))?;
101    Ok((name, op))
102}
103
104pub fn get_name_operator(s: &str) -> Option<(&str, &str, &str)> {
105    regex_captures!(r"(\S+)(\^[-+\d]+)", s)
106}
107
108#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
109pub struct PeeringSet {
110    pub body: String,
111    pub peerings: Vec<Peering>,
112}
113
114#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
115pub struct FilterSet {
116    pub body: String,
117    pub filters: Vec<Filter>,
118}