bgpkit_parser/models/bgp/attributes/
aspath.rs

1use crate::models::*;
2use itertools::Itertools;
3use std::borrow::Cow;
4use std::fmt::{Display, Formatter};
5use std::hash::{Hash, Hasher};
6use std::iter::FromIterator;
7use std::marker::PhantomData;
8use std::mem::discriminant;
9
10/// Enum of AS path segment.
11#[derive(Debug, Clone)]
12pub enum AsPathSegment {
13    AsSequence(Vec<Asn>),
14    AsSet(Vec<Asn>),
15    ConfedSequence(Vec<Asn>),
16    ConfedSet(Vec<Asn>),
17}
18
19impl AsPathSegment {
20    /// Shorthand for creating an `AsSequence` segment.
21    pub fn sequence<S: AsRef<[u32]>>(seq: S) -> Self {
22        AsPathSegment::AsSequence(seq.as_ref().iter().copied().map_into().collect())
23    }
24
25    /// Shorthand for creating an `AsSet` segment.
26    pub fn set<S: AsRef<[u32]>>(seq: S) -> Self {
27        AsPathSegment::AsSet(seq.as_ref().iter().copied().map_into().collect())
28    }
29
30    /// Get the number of ASNs this segment adds to the route. For the number of ASNs within the
31    /// segment use [AsPathSegment::len] instead.
32    pub fn route_len(&self) -> usize {
33        match self {
34            AsPathSegment::AsSequence(v) => v.len(),
35            AsPathSegment::AsSet(_) => 1,
36            AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_) => 0,
37        }
38    }
39
40    /// Ge the total number of ASNs within this segment. For the number of ASNs this segment adds to
41    /// a packet's route, use [AsPathSegment::route_len] instead.
42    pub fn len(&self) -> usize {
43        self.as_ref().len()
44    }
45
46    /// Returns true if this segment has a length of 0.
47    pub fn is_empty(&self) -> bool {
48        self.as_ref().is_empty()
49    }
50
51    /// Get an iterator over the ASNs within this path segment
52    pub fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
53        self.into_iter()
54    }
55
56    /// Get a mutable iterator over the ASNs within this path segment
57    pub fn iter_mut(&mut self) -> <&'_ mut Self as IntoIterator>::IntoIter {
58        self.into_iter()
59    }
60
61    /// Gets if a segment represents the local members of an autonomous system confederation.
62    /// Shorthand for `matches!(x, AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_))`.
63    ///
64    /// <https://datatracker.ietf.org/doc/html/rfc3065#section-5>
65    pub fn is_confed(&self) -> bool {
66        matches!(
67            self,
68            AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_)
69        )
70    }
71
72    /// Merge two [AsPathSegment]s in place and return if the merge was successful.
73    ///
74    /// See [AsPath::coalesce] for more information.
75    fn merge_in_place(&mut self, other: &mut Self) -> bool {
76        use AsPathSegment::*;
77
78        match (self, other) {
79            (AsSequence(x), AsSequence(y)) | (ConfedSequence(x), ConfedSequence(y)) => {
80                x.extend_from_slice(y);
81                true
82            }
83            (x @ (AsSequence(_) | ConfedSequence(_)), y) if x.is_empty() => {
84                std::mem::swap(x, y);
85                true
86            }
87            (_, AsSequence(y) | ConfedSequence(y)) if y.is_empty() => true,
88            _ => false,
89        }
90    }
91
92    /// A much more aggressive version of [AsPathSegment::merge_in_place] which de-duplicates and
93    /// converts sets with only 1 ASN to sequences.
94    ///
95    /// See [AsPath::dedup_coalesce] for more information.
96    fn dedup_merge_in_place(&mut self, other: &mut Self) -> bool {
97        use AsPathSegment::*;
98
99        other.dedup();
100        match (self, other) {
101            (AsSequence(x), AsSequence(y)) | (ConfedSequence(x), ConfedSequence(y)) => {
102                x.extend_from_slice(y);
103                x.dedup();
104                true
105            }
106            (x @ (AsSequence(_) | ConfedSequence(_)), y) if x.is_empty() => {
107                std::mem::swap(x, y);
108                true
109            }
110            (_, AsSequence(y) | ConfedSequence(y)) if y.is_empty() => true,
111            _ => false,
112        }
113    }
114
115    /// Deduplicate ASNs in this path segment. Additionally, sets are sorted and may be converted to
116    /// sequences if they only have a single element.
117    ///
118    /// See [AsPath::dedup_coalesce] for more information.
119    fn dedup(&mut self) {
120        match self {
121            AsPathSegment::AsSequence(x) | AsPathSegment::ConfedSequence(x) => x.dedup(),
122            AsPathSegment::AsSet(x) => {
123                x.sort_unstable();
124                x.dedup();
125                if x.len() == 1 {
126                    *self = AsPathSegment::AsSequence(std::mem::take(x));
127                }
128            }
129            AsPathSegment::ConfedSet(x) => {
130                x.sort_unstable();
131                x.dedup();
132                if x.len() == 1 {
133                    *self = AsPathSegment::ConfedSequence(std::mem::take(x));
134                }
135            }
136        }
137    }
138
139    pub fn to_u32_vec_opt(&self, dedup: bool) -> Option<Vec<u32>> {
140        match self {
141            AsPathSegment::AsSequence(v) => {
142                let mut p: Vec<u32> = v.iter().map(|asn| (*asn).into()).collect();
143                if dedup {
144                    p.dedup();
145                }
146                Some(p)
147            }
148            AsPathSegment::AsSet(v) => {
149                if v.len() == 1 {
150                    // if the segment is an AS_SET and the length is 1, we can consider this a
151                    // single ASN path vector
152                    Some(vec![v[0].into()])
153                } else {
154                    None
155                }
156            }
157            _ => None,
158        }
159    }
160}
161
162impl IntoIterator for AsPathSegment {
163    type Item = Asn;
164    type IntoIter = std::vec::IntoIter<Asn>;
165
166    fn into_iter(self) -> Self::IntoIter {
167        let (AsPathSegment::AsSequence(x)
168        | AsPathSegment::AsSet(x)
169        | AsPathSegment::ConfedSequence(x)
170        | AsPathSegment::ConfedSet(x)) = self;
171        x.into_iter()
172    }
173}
174
175impl<'a> IntoIterator for &'a AsPathSegment {
176    type Item = &'a Asn;
177    type IntoIter = std::slice::Iter<'a, Asn>;
178
179    fn into_iter(self) -> Self::IntoIter {
180        let (AsPathSegment::AsSequence(x)
181        | AsPathSegment::AsSet(x)
182        | AsPathSegment::ConfedSequence(x)
183        | AsPathSegment::ConfedSet(x)) = self;
184        x.iter()
185    }
186}
187
188impl<'a> IntoIterator for &'a mut AsPathSegment {
189    type Item = &'a mut Asn;
190    type IntoIter = std::slice::IterMut<'a, Asn>;
191
192    fn into_iter(self) -> Self::IntoIter {
193        let (AsPathSegment::AsSequence(x)
194        | AsPathSegment::AsSet(x)
195        | AsPathSegment::ConfedSequence(x)
196        | AsPathSegment::ConfedSet(x)) = self;
197        x.iter_mut()
198    }
199}
200
201impl AsRef<[Asn]> for AsPathSegment {
202    fn as_ref(&self) -> &[Asn] {
203        let (AsPathSegment::AsSequence(x)
204        | AsPathSegment::AsSet(x)
205        | AsPathSegment::ConfedSequence(x)
206        | AsPathSegment::ConfedSet(x)) = self;
207        x
208    }
209}
210
211impl Hash for AsPathSegment {
212    fn hash<H: Hasher>(&self, state: &mut H) {
213        // Hash the discriminant since we do not differentiate between confederation segments
214        discriminant(self).hash(state);
215
216        let set = match self {
217            AsPathSegment::AsSequence(x) | AsPathSegment::ConfedSequence(x) => {
218                return x.hash(state)
219            }
220            AsPathSegment::AsSet(x) | AsPathSegment::ConfedSet(x) => x,
221        };
222
223        // FIXME: Once is_sorted is stabilized, call it first to determine if sorting is required
224        if set.len() <= 32 {
225            let mut buffer = [Asn::new_32bit(0); 32];
226            set.iter()
227                .zip(&mut buffer)
228                .for_each(|(asn, buffer)| *buffer = *asn);
229
230            let slice = &mut buffer[..set.len()];
231            slice.sort_unstable();
232            Asn::hash_slice(slice, state);
233            return;
234        }
235
236        // Fallback to allocating a Vec on the heap to sort
237        set.iter().sorted().for_each(|x| x.hash(state));
238    }
239}
240
241/// Check for equality of two path segments.
242/// ```rust
243/// # use bgpkit_parser::models::AsPathSegment;
244/// let a = AsPathSegment::sequence([1, 2, 3]);
245/// let b = AsPathSegment::set([1, 2, 3]);
246///
247/// // Sequences must be identical to be considered equivalent
248/// assert_eq!(a, AsPathSegment::sequence([1, 2, 3]));
249/// assert_ne!(a, AsPathSegment::sequence([1, 2, 3, 3]));
250///
251/// // Sets may be reordered, but must contain exactly the same ASNs.
252/// assert_eq!(b, AsPathSegment::set([3, 1, 2]));
253/// assert_ne!(b, AsPathSegment::set([1, 2, 3, 3]));
254/// ```
255impl PartialEq for AsPathSegment {
256    fn eq(&self, other: &Self) -> bool {
257        let (x, y) = match (self, other) {
258            (AsPathSegment::AsSequence(x), AsPathSegment::AsSequence(y))
259            | (AsPathSegment::ConfedSequence(x), AsPathSegment::ConfedSequence(y)) => {
260                return x == y
261            }
262            (AsPathSegment::AsSet(x), AsPathSegment::AsSet(y))
263            | (AsPathSegment::ConfedSet(x), AsPathSegment::ConfedSet(y)) => (x, y),
264            _ => return false,
265        };
266
267        // Attempt to exit early
268        if x.len() != y.len() {
269            return false;
270        } else if x == y {
271            return true;
272        }
273
274        if x.len() <= 32 {
275            let mut x_buffer = [Asn::new_32bit(0); 32];
276            let mut y_buffer = [Asn::new_32bit(0); 32];
277            x.iter()
278                .zip(&mut x_buffer)
279                .for_each(|(asn, buffer)| *buffer = *asn);
280            y.iter()
281                .zip(&mut y_buffer)
282                .for_each(|(asn, buffer)| *buffer = *asn);
283
284            x_buffer[..x.len()].sort_unstable();
285            y_buffer[..y.len()].sort_unstable();
286            return x_buffer[..x.len()] == y_buffer[..y.len()];
287        }
288
289        x.iter()
290            .sorted()
291            .zip(y.iter().sorted())
292            .all(|(a, b)| a == b)
293    }
294}
295
296impl Eq for AsPathSegment {}
297
298/// This is not a perfect solution since it is theoretically possible that a path could be created
299/// with more variations than a u64. That being said, the chances of such a thing occurring are
300/// essentially non-existent unless a BGP peer begins announcing maliciously constructed paths.
301struct AsPathNumberedRouteIter<'a> {
302    path: &'a [AsPathSegment],
303    index: usize,
304    route_num: u64,
305}
306
307impl Iterator for AsPathNumberedRouteIter<'_> {
308    type Item = Asn;
309
310    fn next(&mut self) -> Option<Self::Item> {
311        loop {
312            match self.path.first()? {
313                AsPathSegment::AsSequence(x) => match x.get(self.index) {
314                    None => {
315                        self.index = 0;
316                        self.path = &self.path[1..];
317                    }
318                    Some(asn) => {
319                        self.index += 1;
320                        return Some(*asn);
321                    }
322                },
323                AsPathSegment::AsSet(x) => {
324                    self.path = &self.path[1..];
325                    if x.is_empty() {
326                        return Some(Asn::RESERVED);
327                    }
328
329                    let asn = x[(self.route_num % x.len() as u64) as usize];
330                    self.route_num /= x.len() as u64;
331                    return Some(asn);
332                }
333                _ => self.path = &self.path[1..],
334            }
335        }
336    }
337}
338
339pub struct AsPathRouteIter<'a, D> {
340    path: Cow<'a, [AsPathSegment]>,
341    route_num: u64,
342    total_routes: u64,
343    _phantom: PhantomData<D>,
344}
345
346impl<D> Iterator for AsPathRouteIter<'_, D>
347where
348    D: FromIterator<Asn>,
349{
350    type Item = D;
351
352    fn next(&mut self) -> Option<Self::Item> {
353        if self.route_num >= self.total_routes {
354            return None;
355        }
356
357        // Attempt to speed up what is by far the most common case (a path of a single sequence)
358        if self.route_num == 0 && self.path.len() == 1 {
359            if let AsPathSegment::AsSequence(sequence) = &self.path[0] {
360                let route = D::from_iter(sequence.iter().copied());
361                self.route_num += 1;
362                return Some(route);
363            }
364        }
365
366        let route_asn_iter = AsPathNumberedRouteIter {
367            path: self.path.as_ref(),
368            index: 0,
369            route_num: self.route_num,
370        };
371
372        self.route_num += 1;
373        Some(D::from_iter(route_asn_iter))
374    }
375}
376
377#[derive(Debug, PartialEq, Clone, Eq, Default, Hash)]
378pub struct AsPath {
379    pub segments: Vec<AsPathSegment>,
380}
381
382// Define iterator type aliases. The storage mechanism and by extension the iterator types may
383// change later, but these types should remain consistent.
384pub type SegmentIter<'a> = std::slice::Iter<'a, AsPathSegment>;
385pub type SegmentIterMut<'a> = std::slice::IterMut<'a, AsPathSegment>;
386pub type SegmentIntoIter = std::vec::IntoIter<AsPathSegment>;
387
388impl AsPath {
389    pub fn new() -> AsPath {
390        AsPath { segments: vec![] }
391    }
392
393    /// Shorthand for creating an `AsPath` consisting of a single `AsSequence` segment.
394    pub fn from_sequence<S: AsRef<[u32]>>(seq: S) -> Self {
395        let segment = AsPathSegment::AsSequence(seq.as_ref().iter().copied().map_into().collect());
396
397        AsPath {
398            segments: vec![segment],
399        }
400    }
401
402    pub fn from_segments(segments: Vec<AsPathSegment>) -> AsPath {
403        AsPath { segments }
404    }
405
406    /// Adds a new segment to the end of the path. This will change the origin of the path. No
407    /// validation or merging the segment is performed during this step.
408    pub fn append_segment(&mut self, segment: AsPathSegment) {
409        self.segments.push(segment);
410    }
411
412    /// Check if the path is empty. Note that a non-empty path may have a route length of 0 due to
413    /// empty segments or confederation segments.
414    pub fn is_empty(&self) -> bool {
415        self.segments.is_empty()
416    }
417
418    /// Get the total length of the routes this path represents. For example, if this route
419    /// contained a sequence of 5 ASNs followed by a set of 3 ASNs, the total route length would be
420    /// 6.
421    ///
422    /// Confederation segments do not count towards the total route length. This means it is
423    /// possible to have a non-empty AsPath with a length of 0.
424    pub fn route_len(&self) -> usize {
425        self.segments.iter().map(AsPathSegment::route_len).sum()
426    }
427
428    /// Get the number of segments that make up this path. For the number of ASNs in routes
429    /// represented by this path, use [AsPath::route_len].
430    pub fn len(&self) -> usize {
431        self.segments.len()
432    }
433
434    /// Get the total number of routes this path represents. This function assumes the total number
435    /// of route variations can be represented by a u64.
436    pub fn num_route_variations(&self) -> u64 {
437        let mut variations: u64 = 1;
438
439        for segment in &self.segments {
440            if let AsPathSegment::AsSet(x) = segment {
441                variations *= x.len() as u64;
442            }
443        }
444
445        variations
446    }
447
448    /// Checks if any segments of this [AsPath] contain the following ASN.
449    pub fn contains_asn(&self, x: Asn) -> bool {
450        self.iter_segments().flatten().contains(&x)
451    }
452
453    /// Coalesce this [AsPath] into the minimum number of segments required without changing the
454    /// values along the path. This can be helpful as some BGP servers will prepend additional
455    /// segments without coalescing sequences. For de-duplicating see [AsPath::dedup_coalesce].
456    ///
457    /// Changes applied by this function:
458    ///  - Merge adjacent AS_SEQUENCE segments
459    ///  - Merge adjacent AS_CONFED_SEQUENCE segments
460    ///  - Removing empty AS_SEQUENCE and AS_CONFED_SEQUENCE segments
461    ///
462    /// ```rust
463    /// # use bgpkit_parser::models::{AsPath, AsPathSegment};
464    /// let mut a = AsPath::from_segments(vec![
465    ///     AsPathSegment::sequence([]),
466    ///     AsPathSegment::sequence([1, 2]),
467    ///     AsPathSegment::sequence([]),
468    ///     AsPathSegment::sequence([2]),
469    ///     AsPathSegment::set([2]),
470    ///     AsPathSegment::set([5, 3, 3, 2]),
471    /// ]);
472    ///
473    /// let expected = AsPath::from_segments(vec![
474    ///     AsPathSegment::sequence([1, 2, 2]),
475    ///     AsPathSegment::set([2]),
476    ///     AsPathSegment::set([5, 3, 3, 2]),
477    /// ]);
478    ///
479    /// a.coalesce();
480    /// assert_eq!(a, expected);
481    /// ```
482    /// If there is only one segment, no changes will occur. This function will not attempt to
483    /// deduplicate sequences or alter sets.
484    pub fn coalesce(&mut self) {
485        let mut end_index = 0;
486        let mut scan_index = 1;
487
488        while scan_index < self.segments.len() {
489            let (a, b) = self.segments.split_at_mut(scan_index);
490            if !AsPathSegment::merge_in_place(&mut a[end_index], &mut b[0]) {
491                end_index += 1;
492                self.segments.swap(end_index, scan_index);
493            }
494            scan_index += 1;
495        }
496
497        self.segments.truncate(end_index + 1);
498    }
499
500    /// A more aggressive version of [AsPath::coalesce] which also de-duplicates ASNs within this
501    /// path and converts sets of a single ASN to sequences. Some BGP servers will prepend their own
502    /// ASN multiple times when announcing a path to artificially increase the route length and make
503    /// the route seem less less desirable to peers.This function is best suited for use-cases which
504    /// only care about transitions between ASes along the path.
505    ///
506    /// Changes applied by this function:
507    ///  - Merge adjacent AS_SEQUENCE segments
508    ///  - Merge adjacent AS_CONFED_SEQUENCE segments
509    ///  - Removing empty AS_SEQUENCE and AS_CONFED_SEQUENCE segments
510    ///  - De-duplicate ASNs in AS_SEQUENCE and AS_CONFED_SEQUENCE segments
511    ///  - Sort and de-duplicate ASNs in AS_SET and AS_CONFED_SET segments
512    ///  - Convert AS_SET and AS_CONFED_SET segments with exactly 1 element to sequences
513    ///
514    /// ```rust
515    /// # use bgpkit_parser::models::{AsPath, AsPathSegment};
516    /// let mut a = AsPath::from_segments(vec![
517    ///     AsPathSegment::sequence([1, 2]),
518    ///     AsPathSegment::sequence([]),
519    ///     AsPathSegment::sequence([2]),
520    ///     AsPathSegment::set([2]),
521    ///     AsPathSegment::set([5, 3, 3, 2]),
522    /// ]);
523    ///
524    /// let expected = AsPath::from_segments(vec![
525    ///     AsPathSegment::sequence([1, 2]),
526    ///     AsPathSegment::set([2, 3, 5]),
527    /// ]);
528    ///
529    /// a.dedup_coalesce();
530    /// assert_eq!(a, expected);
531    /// ```
532    pub fn dedup_coalesce(&mut self) {
533        if !self.segments.is_empty() {
534            self.segments[0].dedup();
535        }
536        let mut end_index = 0;
537        let mut scan_index = 1;
538
539        while scan_index < self.segments.len() {
540            let (a, b) = self.segments.split_at_mut(scan_index);
541            if !AsPathSegment::dedup_merge_in_place(&mut a[end_index], &mut b[0]) {
542                end_index += 1;
543                self.segments.swap(end_index, scan_index);
544            }
545            scan_index += 1;
546        }
547
548        self.segments.truncate(end_index + 1);
549    }
550
551    /// Checks if two paths correspond to equivalent routes. Unlike `a == b`, this function will
552    /// ignore duplicate ASNs by comparing the coalesced versions of each path.
553    ///
554    /// This is equivalent to [AsPath::eq] after calling [AsPath::dedup_coalesce] on both paths.
555    pub fn has_equivalent_routing(&self, other: &Self) -> bool {
556        let mut a = self.to_owned();
557        let mut b = other.to_owned();
558
559        a.dedup_coalesce();
560        b.dedup_coalesce();
561
562        a == b
563    }
564
565    /// Get the length of ASN required to store all of the ASNs within this path
566    pub fn required_asn_length(&self) -> AsnLength {
567        self.iter_segments().flatten().map(Asn::required_len).fold(
568            AsnLength::Bits16,
569            |a, b| match (a, b) {
570                (AsnLength::Bits16, AsnLength::Bits16) => AsnLength::Bits16,
571                _ => AsnLength::Bits32,
572            },
573        )
574    }
575
576    pub fn iter_segments(&self) -> SegmentIter<'_> {
577        self.segments.iter()
578    }
579
580    pub fn iter_segments_mut(&mut self) -> SegmentIterMut<'_> {
581        self.segments.iter_mut()
582    }
583
584    pub fn into_segments_iter(self) -> SegmentIntoIter {
585        self.segments.into_iter()
586    }
587
588    /// Gets an iterator over all possible routes this path represents.
589    pub fn iter_routes<D>(&self) -> AsPathRouteIter<'_, D>
590    where
591        D: FromIterator<Asn>,
592    {
593        AsPathRouteIter {
594            path: Cow::Borrowed(&self.segments),
595            route_num: 0,
596            total_routes: self.num_route_variations(),
597            _phantom: PhantomData,
598        }
599    }
600
601    /// Construct AsPath from AS_PATH and AS4_PATH
602    ///
603    /// <https://datatracker.ietf.org/doc/html/rfc6793#section-4.2.3>
604    ///
605    /// ```text
606    ///    If the number of AS numbers in the AS_PATH attribute is less than the
607    ///    number of AS numbers in the AS4_PATH attribute, then the AS4_PATH
608    ///    attribute SHALL be ignored, and the AS_PATH attribute SHALL be taken
609    ///    as the AS path information.
610    ///
611    ///    If the number of AS numbers in the AS_PATH attribute is larger than
612    ///    or equal to the number of AS numbers in the AS4_PATH attribute, then
613    ///    the AS path information SHALL be constructed by taking as many AS
614    ///    numbers and path segments as necessary from the leading part of the
615    ///    AS_PATH attribute, and then prepending them to the AS4_PATH attribute
616    ///    so that the AS path information has a number of AS numbers identical
617    ///    to that of the AS_PATH attribute.  Note that a valid
618    ///    AS_CONFED_SEQUENCE or AS_CONFED_SET path segment SHALL be prepended
619    ///    if it is either the leading path segment or is adjacent to a path
620    ///    segment that is prepended.
621    /// ```
622    pub fn merge_aspath_as4path(aspath: &AsPath, as4path: &AsPath) -> AsPath {
623        if aspath.route_len() < as4path.route_len() {
624            // Per RFC6793, if 2-byte AS path is shorter than 4-byte AS path, ignore 4-byte AS path
625            return aspath.clone();
626        }
627
628        let mut as4iter = as4path.segments.iter();
629        let mut new_segs: Vec<AsPathSegment> = vec![];
630
631        for seg in &aspath.segments {
632            match as4iter.next() {
633                None => {
634                    new_segs.push(seg.clone());
635                }
636                Some(as4seg_unwrapped) => {
637                    if let (AsPathSegment::AsSequence(seq), AsPathSegment::AsSequence(seq4)) =
638                        (seg, as4seg_unwrapped)
639                    {
640                        let diff_len = seq.len() as i32 - seq4.len() as i32;
641                        match diff_len {
642                            d if d > 0 => {
643                                // 2-byte ASN path is longer than 4-byte ASN path
644                                // we take the leading part of 2-byte ASN path and prepend it to 4-byte ASN path
645                                let mut new_seq: Vec<Asn> = vec![];
646                                new_seq.extend(seq.iter().take(d as usize));
647                                new_seq.extend(seq4);
648                                new_segs.push(AsPathSegment::AsSequence(new_seq));
649                            }
650                            d if d < 0 => {
651                                new_segs.push(AsPathSegment::AsSequence(seq.clone()));
652                            }
653                            _ => {
654                                new_segs.push(AsPathSegment::AsSequence(seq4.clone()));
655                            }
656                        }
657                    } else {
658                        new_segs.push(as4seg_unwrapped.clone());
659                    }
660                }
661            };
662        }
663
664        AsPath { segments: new_segs }
665    }
666
667    /// Iterate through the originating ASNs of this path. This functionality is provided for
668    /// completeness, but in almost all cases this iterator should only contain a single element.
669    pub fn iter_origins(&self) -> impl '_ + Iterator<Item = Asn> {
670        let origin_slice = match self.segments.last() {
671            Some(AsPathSegment::AsSequence(v)) => v.last().map(std::slice::from_ref).unwrap_or(&[]),
672            Some(AsPathSegment::AsSet(v)) => v.as_ref(),
673            _ => &[],
674        };
675
676        origin_slice.iter().copied()
677    }
678
679    /// This function serves as a alternative to [AsPath::iter_origins] which attempts to make the
680    /// assumption that a path can only have exactly one origin. If a path does not have exactly 1
681    /// origin (such as when empty or ending in a set), then `None` will be returned instead.
682    pub fn get_origin_opt(&self) -> Option<Asn> {
683        match self.segments.last() {
684            Some(AsPathSegment::AsSequence(v)) => v.last().copied(),
685            Some(AsPathSegment::AsSet(v)) if v.len() == 1 => Some(v[0]),
686            _ => None,
687        }
688    }
689
690    /// This function optionally returns the first hop in the AS hop, which is considered as the
691    /// collector ASN of the message.
692    pub fn get_collector_opt(&self) -> Option<Asn> {
693        match self.segments.first() {
694            Some(AsPathSegment::AsSequence(v)) => v.first().copied(),
695            Some(AsPathSegment::AsSet(v)) if v.len() == 1 => Some(v[0]),
696            _ => None,
697        }
698    }
699
700    pub fn to_u32_vec_opt(&self, dedup: bool) -> Option<Vec<u32>> {
701        let mut path = vec![];
702
703        // Iterate over the segments in reverse order
704        for seg in self.segments.iter().rev() {
705            if let Some(p) = seg.to_u32_vec_opt(dedup) {
706                // for each segment, we also reverse the order of ASNs so that we will eventually
707                // get a reversed AS path stored in `path`.
708                path.extend(p.iter().rev());
709            } else {
710                // If we encounter a segment that cannot be converted to a u32, we return None.
711                // This is because the path is not a simple sequence of ASNs, and returning partial
712                // AS path would be misleading.
713                return None;
714            }
715        }
716
717        match path.is_empty() {
718            true => {
719                // empty path is not a valid AS path
720                None
721            }
722            false => {
723                // reverse the order of ASNs to get the original path
724                path.reverse();
725                Some(path)
726            }
727        }
728    }
729}
730
731/// Iterates over all route variations the given `AsPath` represents.
732impl<'a> IntoIterator for &'a AsPath {
733    type Item = Vec<Asn>;
734    type IntoIter = AsPathRouteIter<'a, Vec<Asn>>;
735
736    fn into_iter(self) -> Self::IntoIter {
737        self.iter_routes()
738    }
739}
740
741/// Iterates over all route variations the given `AsPath` represents.
742impl IntoIterator for AsPath {
743    type Item = Vec<Asn>;
744    type IntoIter = AsPathRouteIter<'static, Vec<Asn>>;
745
746    fn into_iter(self) -> Self::IntoIter {
747        AsPathRouteIter {
748            total_routes: self.num_route_variations(),
749            path: Cow::Owned(self.segments),
750            route_num: 0,
751            _phantom: PhantomData,
752        }
753    }
754}
755
756impl Display for AsPath {
757    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
758        for (index, segment) in self.iter_segments().enumerate() {
759            if index != 0 {
760                write!(f, " ")?;
761            }
762
763            match segment {
764                AsPathSegment::AsSequence(v) | AsPathSegment::ConfedSequence(v) => {
765                    let mut asn_iter = v.iter();
766                    if let Some(first_element) = asn_iter.next() {
767                        write!(f, "{}", first_element)?;
768
769                        for asn in asn_iter {
770                            write!(f, " {}", asn)?;
771                        }
772                    }
773                }
774                AsPathSegment::AsSet(v) | AsPathSegment::ConfedSet(v) => {
775                    write!(f, "{{")?;
776                    let mut asn_iter = v.iter();
777                    if let Some(first_element) = asn_iter.next() {
778                        write!(f, "{}", first_element)?;
779
780                        for asn in asn_iter {
781                            write!(f, ",{}", asn)?;
782                        }
783                    }
784                    write!(f, "}}")?;
785                }
786            }
787        }
788
789        Ok(())
790    }
791}
792
793#[cfg(feature = "serde")]
794mod serde_impl {
795    use super::*;
796    use serde::de::{SeqAccess, Visitor};
797    use serde::ser::SerializeSeq;
798    use serde::{Deserialize, Deserializer, Serialize, Serializer};
799    use std::borrow::Cow;
800
801    /// Segment type names using names from RFC3065.
802    ///
803    /// <https://datatracker.ietf.org/doc/html/rfc3065#section-5>
804    #[allow(non_camel_case_types)]
805    #[derive(Serialize, Deserialize)]
806    enum SegmentType {
807        AS_SET,
808        AS_SEQUENCE,
809        AS_CONFED_SEQUENCE,
810        AS_CONFED_SET,
811    }
812
813    #[derive(Serialize, Deserialize)]
814    struct VerboseSegment<'s> {
815        ty: SegmentType,
816        values: Cow<'s, [Asn]>,
817    }
818
819    impl Serialize for AsPathSegment {
820        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
821        where
822            S: Serializer,
823        {
824            let (ty, elements) = match self {
825                AsPathSegment::AsSequence(x) => (SegmentType::AS_SEQUENCE, x.as_ref()),
826                AsPathSegment::AsSet(x) => (SegmentType::AS_SET, x.as_ref()),
827                AsPathSegment::ConfedSequence(x) => (SegmentType::AS_CONFED_SEQUENCE, x.as_ref()),
828                AsPathSegment::ConfedSet(x) => (SegmentType::AS_CONFED_SET, x.as_ref()),
829            };
830
831            let verbose = VerboseSegment {
832                ty,
833                values: Cow::Borrowed(elements),
834            };
835
836            verbose.serialize(serializer)
837        }
838    }
839
840    impl<'de> Deserialize<'de> for AsPathSegment {
841        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
842        where
843            D: Deserializer<'de>,
844        {
845            let verbose = VerboseSegment::deserialize(deserializer)?;
846
847            let values = verbose.values.into_owned();
848            match verbose.ty {
849                SegmentType::AS_SET => Ok(AsPathSegment::AsSet(values)),
850                SegmentType::AS_SEQUENCE => Ok(AsPathSegment::AsSequence(values)),
851                SegmentType::AS_CONFED_SEQUENCE => Ok(AsPathSegment::ConfedSequence(values)),
852                SegmentType::AS_CONFED_SET => Ok(AsPathSegment::ConfedSet(values)),
853            }
854        }
855    }
856
857    /// Check if we can serialize an `AsPath` using the simplified format and get the number of
858    /// elements to do so. The ambiguities that could prevent us from doing so are confederation
859    /// segments and adjacent sequence segments.
860    fn simplified_format_len(segments: &[AsPathSegment]) -> Option<usize> {
861        let mut elements = 0;
862        let mut prev_was_sequence = false;
863        for segment in segments {
864            match segment {
865                AsPathSegment::AsSequence(seq) if !prev_was_sequence => {
866                    prev_was_sequence = true;
867                    elements += seq.len();
868                }
869                AsPathSegment::AsSet(_) => {
870                    prev_was_sequence = false;
871                    elements += 1;
872                }
873                _ => return None,
874            }
875        }
876
877        Some(elements)
878    }
879
880    /// # Serialization format
881    /// For the sake of readability and ease of use within other applications, there are verbose and
882    /// simplified variants for serialization.
883    ///
884    /// ## Simplified format
885    /// The simplified format is the default preferred serialization format. This format does not
886    /// cover confederation segments and involves a single list of ASNs within the path sequence.
887    /// For sets, a list of set members is used in place of an ASN.
888    /// ```rust
889    /// # use bgpkit_parser::models::{Asn, AsPath};
890    /// # use bgpkit_parser::models::AsPathSegment::*;
891    ///
892    /// let a: AsPath = serde_json::from_str("[123, 942, 102]").unwrap();
893    /// let b: AsPath = serde_json::from_str("[231, 432, [643, 836], 352]").unwrap();
894    ///
895    /// assert_eq!(&a.segments, &[
896    ///     AsSequence(vec![Asn::from(123), Asn::from(942), Asn::from(102)])
897    /// ]);
898    /// assert_eq!(&b.segments, &[
899    ///     AsSequence(vec![Asn::from(231), Asn::from(432)]),
900    ///     AsSet(vec![Asn::from(643), Asn::from(836)]),
901    ///     AsSequence(vec![Asn::from(352)])
902    /// ]);
903    /// ```
904    ///
905    /// ## Verbose format
906    /// The verbose format serves as the fallback format for when the simplified format can not be
907    /// used due to ambiguity. This happens when confederation segments are present, or multiple
908    /// sequences occur back to back. In this format, segments are explicitly seperated and labeled.
909    /// Segment types, denoted by the `ty` field, correspond to the names used within RFC3065
910    /// (`AS_SET`, `AS_SEQUENCE`, `AS_CONFED_SEQUENCE`, `AS_CONFED_SET`).
911    /// ```rust
912    /// # use bgpkit_parser::models::{Asn, AsPath};
913    /// # use bgpkit_parser::models::AsPathSegment::*;
914    ///
915    /// let a = r#"[
916    ///     { "ty": "AS_CONFED_SEQUENCE", "values": [123, 942] },
917    ///     { "ty": "AS_SEQUENCE", "values": [773] },
918    ///     { "ty": "AS_SEQUENCE", "values": [382, 293] }
919    /// ]"#;
920    ///
921    /// let parsed: AsPath = serde_json::from_str(a).unwrap();
922    /// assert_eq!(&parsed.segments, &[
923    ///     ConfedSequence(vec![Asn::from(123), Asn::from(942)]),
924    ///     AsSequence(vec![Asn::from(773)]),
925    ///     AsSequence(vec![Asn::from(382), Asn::from(293)])
926    /// ]);
927    /// ```
928    impl Serialize for AsPath {
929        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
930        where
931            S: Serializer,
932        {
933            if let Some(num_elements) = simplified_format_len(&self.segments) {
934                // Serialize simplified format
935                let mut seq_serializer = serializer.serialize_seq(Some(num_elements))?;
936
937                for segment in &self.segments {
938                    match segment {
939                        AsPathSegment::AsSequence(elements) => {
940                            elements
941                                .iter()
942                                .try_for_each(|x| seq_serializer.serialize_element(x))?;
943                        }
944                        AsPathSegment::AsSet(x) => seq_serializer.serialize_element(x)?,
945                        _ => unreachable!("simplified_format_len checked for confed segments"),
946                    }
947                }
948                return seq_serializer.end();
949            }
950
951            // Serialize verbose format
952            serializer.collect_seq(&self.segments)
953        }
954    }
955
956    struct AsPathVisitor;
957
958    impl<'de> Visitor<'de> for AsPathVisitor {
959        type Value = AsPath;
960
961        fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
962            formatter.write_str("list of AS_PATH segments")
963        }
964
965        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
966        where
967            A: SeqAccess<'de>,
968        {
969            // Technically, we can handle an input that mixes the simplified and verbose formats,
970            // but we do not want to document this behavior as it may change in future updates.
971            #[derive(Deserialize)]
972            #[serde(untagged)]
973            enum PathElement {
974                SequenceElement(Asn),
975                Set(Vec<Asn>),
976                Verbose(AsPathSegment),
977            }
978
979            let mut append_new_sequence = false;
980            let mut segments = Vec::new();
981            while let Some(element) = seq.next_element()? {
982                match element {
983                    PathElement::SequenceElement(x) => {
984                        if append_new_sequence {
985                            // If the input is mixed between verbose and regular segments, this flag
986                            // is used to prevent appending to a verbose sequence.
987                            append_new_sequence = false;
988                            segments.push(AsPathSegment::AsSequence(Vec::new()));
989                        }
990
991                        if let Some(AsPathSegment::AsSequence(last_sequence)) = segments.last_mut()
992                        {
993                            last_sequence.push(x);
994                        } else {
995                            segments.push(AsPathSegment::AsSequence(vec![x]));
996                        }
997                    }
998                    PathElement::Set(values) => {
999                        segments.push(AsPathSegment::AsSet(values));
1000                    }
1001                    PathElement::Verbose(verbose) => {
1002                        segments.push(verbose);
1003                    }
1004                }
1005            }
1006
1007            Ok(AsPath { segments })
1008        }
1009    }
1010
1011    impl<'de> Deserialize<'de> for AsPath {
1012        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1013        where
1014            D: Deserializer<'de>,
1015        {
1016            deserializer.deserialize_seq(AsPathVisitor)
1017        }
1018    }
1019}
1020
1021#[cfg(test)]
1022mod tests {
1023    use crate::models::*;
1024    use itertools::Itertools;
1025    use std::collections::HashSet;
1026
1027    #[test]
1028    fn test_aspath_as4path_merge() {
1029        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1030        let as4path = AsPath::from_sequence([2, 3, 7]);
1031        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1032        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 3, 7]));
1033
1034        let aspath = AsPath::from_sequence([1, 2]);
1035        let as4path = AsPath::from_sequence([2, 3, 7]);
1036        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1037        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2]));
1038
1039        // when the sequence length is the same, the as4path should be used
1040        let aspath = AsPath::from_sequence([1, 2]);
1041        let as4path = AsPath::from_sequence([3, 4]);
1042        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1043        assert_eq!(newpath.segments[0], AsPathSegment::sequence([3, 4]));
1044
1045        let aspath = AsPath::from_segments(vec![
1046            AsPathSegment::sequence([1, 2, 3, 5]),
1047            AsPathSegment::set([7, 8]),
1048        ]);
1049        let as4path = AsPath::from_sequence([6, 7, 8]);
1050        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1051        assert_eq!(newpath.segments.len(), 2);
1052        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 6, 7, 8]));
1053        assert_eq!(newpath.segments[1], AsPathSegment::set([7, 8]));
1054
1055        let aspath = AsPath::from_segments(vec![
1056            AsPathSegment::sequence([1, 2]),
1057            AsPathSegment::sequence([3, 5]),
1058            AsPathSegment::set([13, 14]),
1059        ]);
1060        let as4path = AsPath::from_segments(vec![
1061            AsPathSegment::sequence([8, 4, 6]),
1062            AsPathSegment::set([11, 12]),
1063        ]);
1064        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1065        assert_eq!(newpath.segments.len(), 3);
1066        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2]));
1067        assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
1068        assert_eq!(newpath.segments[2], AsPathSegment::set([13, 14]));
1069
1070        let aspath = AsPath::from_segments(vec![
1071            AsPathSegment::sequence([1, 2, 3]),
1072            AsPathSegment::sequence([5]),
1073            AsPathSegment::set([13, 14]),
1074        ]);
1075        let as4path = AsPath::from_segments(vec![
1076            AsPathSegment::sequence([7, 8]),
1077            AsPathSegment::set([11, 12]),
1078        ]);
1079        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1080        assert_eq!(newpath.segments.len(), 3);
1081        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 7, 8]));
1082        assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
1083        assert_eq!(newpath.segments[2], AsPathSegment::set([13, 14]));
1084    }
1085
1086    #[test]
1087    fn test_get_origin() {
1088        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1089        let origin = aspath.get_origin_opt();
1090        assert_eq!(origin.unwrap(), 5);
1091
1092        let aspath = AsPath::from_segments(vec![AsPathSegment::set([1, 2, 3, 5])]);
1093        let origin = aspath.get_origin_opt();
1094        assert!(origin.is_none());
1095
1096        let aspath = AsPath::from_segments(vec![AsPathSegment::set([1])]);
1097        let origin = aspath.get_origin_opt();
1098        assert_eq!(origin.unwrap(), 1);
1099
1100        let aspath = AsPath::from_segments(vec![
1101            AsPathSegment::sequence([1, 2, 3, 5]),
1102            AsPathSegment::set([7, 8]),
1103        ]);
1104        let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
1105        assert_eq!(origins, vec![7, 8]);
1106
1107        let aspath = AsPath::from_segments(vec![
1108            AsPathSegment::sequence([1, 2, 3, 5]),
1109            AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)]),
1110        ]);
1111        let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
1112        assert_eq!(origins, Vec::<u32>::new());
1113    }
1114
1115    #[test]
1116    fn test_get_collector() {
1117        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1118        let collector = aspath.get_collector_opt();
1119        assert_eq!(collector.unwrap(), 1);
1120
1121        let aspath = AsPath::from_segments(vec![AsPathSegment::set([7])]);
1122        let collector = aspath.get_collector_opt();
1123        assert_eq!(collector.unwrap(), 7);
1124
1125        let aspath = AsPath::from_segments(vec![AsPathSegment::set([7, 8])]);
1126        let collector = aspath.get_collector_opt();
1127        assert!(collector.is_none());
1128    }
1129
1130    #[test]
1131    fn test_aspath_route_iter() {
1132        let path = AsPath::from_segments(vec![AsPathSegment::sequence([3, 4])]);
1133        let mut routes = HashSet::new();
1134        for route in &path {
1135            assert!(routes.insert(route));
1136        }
1137        assert_eq!(1, routes.len());
1138
1139        let path = AsPath::from_segments(vec![
1140            AsPathSegment::set([3, 4]),
1141            AsPathSegment::set([5, 6]),
1142            AsPathSegment::sequence([7, 8]),
1143            AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)]),
1144            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(9)]),
1145        ]);
1146        assert_eq!(path.route_len(), 4);
1147
1148        let mut routes = HashSet::new();
1149        for route in &path {
1150            assert!(routes.insert(route));
1151        }
1152
1153        assert_eq!(routes.len(), 4);
1154        assert!(routes.contains(&vec![
1155            Asn::from(3),
1156            Asn::from(5),
1157            Asn::from(7),
1158            Asn::from(8)
1159        ]));
1160        assert!(routes.contains(&vec![
1161            Asn::from(3),
1162            Asn::from(6),
1163            Asn::from(7),
1164            Asn::from(8)
1165        ]));
1166        assert!(routes.contains(&vec![
1167            Asn::from(4),
1168            Asn::from(5),
1169            Asn::from(7),
1170            Asn::from(8)
1171        ]));
1172        assert!(routes.contains(&vec![
1173            Asn::from(4),
1174            Asn::from(6),
1175            Asn::from(7),
1176            Asn::from(8)
1177        ]));
1178    }
1179
1180    #[test]
1181    fn test_segment() {
1182        let path_segment = AsPathSegment::sequence([1, 2, 3, 4]);
1183        assert_eq!(path_segment.len(), 4);
1184
1185        // test iter
1186        let mut iter = path_segment.iter();
1187        assert_eq!(iter.next(), Some(&Asn::new_32bit(1)));
1188        assert_eq!(iter.next(), Some(&Asn::new_32bit(2)));
1189        assert_eq!(iter.next(), Some(&Asn::new_32bit(3)));
1190        assert_eq!(iter.next(), Some(&Asn::new_32bit(4)));
1191        assert_eq!(iter.next(), None);
1192
1193        // test iter_mut
1194        let mut path_segment = AsPathSegment::sequence([1]);
1195        let mut iter_mut = path_segment.iter_mut();
1196        assert_eq!(iter_mut.next(), Some(&mut Asn::new_32bit(1)));
1197        assert_eq!(iter_mut.next(), None);
1198
1199        // test is_confed
1200        assert!(AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)]).is_confed());
1201        assert!(AsPathSegment::ConfedSet(vec![Asn::new_32bit(1)]).is_confed());
1202    }
1203
1204    #[test]
1205    fn test_coalesce() {
1206        let mut a = AsPath::from_segments(vec![
1207            AsPathSegment::sequence([]),
1208            AsPathSegment::sequence([1, 2]),
1209            AsPathSegment::sequence([]),
1210            AsPathSegment::sequence([2]),
1211            AsPathSegment::set([2]),
1212            AsPathSegment::set([5, 3, 3, 2]),
1213        ]);
1214
1215        let expected = AsPath::from_segments(vec![
1216            AsPathSegment::sequence([1, 2, 2]),
1217            AsPathSegment::set([2]),
1218            AsPathSegment::set([5, 3, 3, 2]),
1219        ]);
1220
1221        a.coalesce();
1222        assert_eq!(a, expected);
1223    }
1224
1225    #[test]
1226    fn test_confed_set_dedup() {
1227        let mut path_segment = AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(1)]);
1228        path_segment.dedup();
1229        assert_eq!(
1230            path_segment,
1231            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)])
1232        );
1233
1234        let mut path_segment = AsPathSegment::ConfedSet(vec![
1235            Asn::new_32bit(1),
1236            Asn::new_32bit(2),
1237            Asn::new_32bit(2),
1238        ]);
1239        path_segment.dedup();
1240        assert_eq!(
1241            path_segment,
1242            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)])
1243        );
1244    }
1245
1246    #[test]
1247    fn test_path_to_u32() {
1248        // regular sequence
1249        let path_segment = AsPathSegment::sequence([1, 2, 3, 3]);
1250        assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1, 2, 3, 3]));
1251        assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1, 2, 3]));
1252
1253        // regular set: should return None
1254        let path_segment = AsPathSegment::set([1, 2, 3, 3]);
1255        assert_eq!(path_segment.to_u32_vec_opt(false), None);
1256        assert_eq!(path_segment.to_u32_vec_opt(true), None);
1257
1258        // singular set: should be converted to singleton vector
1259        let path_segment = AsPathSegment::set([1]);
1260        assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1]));
1261        assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1]));
1262
1263        // combination of a sequence and a few singleton sets, should be merged into a single vector
1264        let as_path = AsPath::from_segments(vec![
1265            AsPathSegment::set([4]),
1266            AsPathSegment::sequence([2, 3, 3]),
1267            AsPathSegment::set([1]),
1268        ]);
1269        assert_eq!(as_path.to_u32_vec_opt(false), Some(vec![4, 2, 3, 3, 1]));
1270        assert_eq!(as_path.to_u32_vec_opt(true), Some(vec![4, 2, 3, 1]));
1271
1272        // should the path containing any non-convertible segments, return None
1273        let as_path = AsPath::from_segments(vec![
1274            AsPathSegment::set([4, 2]),
1275            AsPathSegment::sequence([2, 3, 3]),
1276            AsPathSegment::set([1]),
1277        ]);
1278        assert_eq!(as_path.to_u32_vec_opt(false), None);
1279        assert_eq!(as_path.to_u32_vec_opt(true), None);
1280
1281        // other corner cases
1282
1283        // empty path
1284        let as_path = AsPath::from_segments(vec![]);
1285        assert_eq!(as_path.to_u32_vec_opt(false), None);
1286        assert_eq!(as_path.to_u32_vec_opt(true), None);
1287
1288        // path with federation segments
1289        let as_path = AsPath::from_segments(vec![
1290            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)]),
1291            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(3), Asn::new_32bit(4)]),
1292        ]);
1293        assert_eq!(as_path.to_u32_vec_opt(false), None);
1294        assert_eq!(as_path.to_u32_vec_opt(true), None);
1295    }
1296
1297    #[test]
1298    fn test_as_ref() {
1299        let path_segment = AsPathSegment::sequence([1, 2]);
1300        assert_eq!(
1301            path_segment.as_ref(),
1302            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1303        );
1304
1305        let path_segment = AsPathSegment::set([1, 2]);
1306        assert_eq!(
1307            path_segment.as_ref(),
1308            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1309        );
1310
1311        let path_segment =
1312            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1), Asn::new_32bit(2)]);
1313        assert_eq!(
1314            path_segment.as_ref(),
1315            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1316        );
1317
1318        let path_segment = AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)]);
1319        assert_eq!(
1320            path_segment.as_ref(),
1321            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1322        );
1323    }
1324
1325    #[test]
1326    fn test_hashing() {
1327        let path_segment = AsPathSegment::sequence([1, 2]);
1328        let path_segment2 = AsPathSegment::sequence([1, 2]);
1329
1330        let hashset = std::iter::once(path_segment).collect::<HashSet<_>>();
1331        assert!(hashset.contains(&path_segment2));
1332    }
1333
1334    #[test]
1335    fn test_equality() {
1336        let path_segment = AsPathSegment::sequence([1, 2]);
1337        let path_segment2 = AsPathSegment::sequence([1, 2]);
1338
1339        assert_eq!(path_segment, path_segment2);
1340
1341        let path_segment = AsPathSegment::sequence([1, 2]);
1342        let path_segment2 = AsPathSegment::set([1, 2, 3]);
1343        assert_ne!(path_segment, path_segment2);
1344
1345        // test equality of AS path longer than 32 ASNs
1346        let path_segment = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
1347        let path_segment2 = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
1348        assert_eq!(path_segment, path_segment2);
1349    }
1350
1351    #[test]
1352    fn test_as_path_display() {
1353        let path = AsPath::from_segments(vec![
1354            AsPathSegment::sequence([1, 2]),
1355            AsPathSegment::set([3, 4]),
1356            AsPathSegment::sequence([5, 6]),
1357            AsPathSegment::ConfedSet(vec![Asn::new_32bit(7)]),
1358            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(8)]),
1359        ]);
1360
1361        assert_eq!(path.to_string(), "1 2 {3,4} 5 6 {7} 8");
1362    }
1363}