Skip to main content

bgpkit_parser/models/bgp/attributes/
aspath.rs

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