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 let Some(p) = seg.to_u32_vec_opt(dedup) {
726                // for each segment, we also reverse the order of ASNs so that we will eventually
727                // get a reversed AS path stored in `path`.
728                path.extend(p.iter().rev());
729            } else {
730                // If we encounter a segment that cannot be converted to a u32, we return None.
731                // This is because the path is not a simple sequence of ASNs, and returning partial
732                // AS path would be misleading.
733                return None;
734            }
735        }
736
737        match path.is_empty() {
738            true => {
739                // empty path is not a valid AS path
740                None
741            }
742            false => {
743                // reverse the order of ASNs to get the original path
744                path.reverse();
745                Some(path)
746            }
747        }
748    }
749}
750
751/// Iterates over all route variations the given `AsPath` represents.
752impl<'a> IntoIterator for &'a AsPath {
753    type Item = Vec<Asn>;
754    type IntoIter = AsPathRouteIter<'a, Vec<Asn>>;
755
756    fn into_iter(self) -> Self::IntoIter {
757        self.iter_routes()
758    }
759}
760
761/// Iterates over all route variations the given `AsPath` represents.
762impl IntoIterator for AsPath {
763    type Item = Vec<Asn>;
764    type IntoIter = AsPathRouteIter<'static, Vec<Asn>>;
765
766    fn into_iter(self) -> Self::IntoIter {
767        AsPathRouteIter {
768            total_routes: self.num_route_variations(),
769            path: Cow::Owned(self.segments.into_vec()),
770            route_num: 0,
771            _phantom: PhantomData,
772        }
773    }
774}
775
776impl Display for AsPath {
777    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
778        for (index, segment) in self.iter_segments().enumerate() {
779            if index != 0 {
780                write!(f, " ")?;
781            }
782
783            match segment {
784                AsPathSegment::AsSequence(v) | AsPathSegment::ConfedSequence(v) => {
785                    let mut asn_iter = v.iter();
786                    if let Some(first_element) = asn_iter.next() {
787                        write!(f, "{first_element}")?;
788
789                        for asn in asn_iter {
790                            write!(f, " {asn}")?;
791                        }
792                    }
793                }
794                AsPathSegment::AsSet(v) | AsPathSegment::ConfedSet(v) => {
795                    write!(f, "{{")?;
796                    let mut asn_iter = v.iter();
797                    if let Some(first_element) = asn_iter.next() {
798                        write!(f, "{first_element}")?;
799
800                        for asn in asn_iter {
801                            write!(f, ",{asn}")?;
802                        }
803                    }
804                    write!(f, "}}")?;
805                }
806            }
807        }
808
809        Ok(())
810    }
811}
812
813#[cfg(feature = "serde")]
814mod serde_impl {
815    use super::*;
816    use serde::de::{SeqAccess, Visitor};
817    use serde::ser::SerializeSeq;
818    use serde::{Deserialize, Deserializer, Serialize, Serializer};
819    use std::borrow::Cow;
820
821    /// Segment type names using names from RFC3065.
822    ///
823    /// <https://datatracker.ietf.org/doc/html/rfc3065#section-5>
824    #[allow(non_camel_case_types)]
825    #[derive(Serialize, Deserialize)]
826    enum SegmentType {
827        AS_SET,
828        AS_SEQUENCE,
829        AS_CONFED_SEQUENCE,
830        AS_CONFED_SET,
831    }
832
833    #[derive(Serialize, Deserialize)]
834    struct VerboseSegment<'s> {
835        ty: SegmentType,
836        values: Cow<'s, [Asn]>,
837    }
838
839    impl Serialize for AsPathSegment {
840        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
841        where
842            S: Serializer,
843        {
844            let (ty, elements) = match self {
845                AsPathSegment::AsSequence(x) => (SegmentType::AS_SEQUENCE, x.as_ref()),
846                AsPathSegment::AsSet(x) => (SegmentType::AS_SET, x.as_ref()),
847                AsPathSegment::ConfedSequence(x) => (SegmentType::AS_CONFED_SEQUENCE, x.as_ref()),
848                AsPathSegment::ConfedSet(x) => (SegmentType::AS_CONFED_SET, x.as_ref()),
849            };
850
851            let verbose = VerboseSegment {
852                ty,
853                values: Cow::Borrowed(elements),
854            };
855
856            verbose.serialize(serializer)
857        }
858    }
859
860    impl<'de> Deserialize<'de> for AsPathSegment {
861        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
862        where
863            D: Deserializer<'de>,
864        {
865            let verbose = VerboseSegment::deserialize(deserializer)?;
866
867            let values: SmallVec<[Asn; 6]> = verbose.values.into_owned().into();
868            match verbose.ty {
869                SegmentType::AS_SET => Ok(AsPathSegment::AsSet(values)),
870                SegmentType::AS_SEQUENCE => Ok(AsPathSegment::AsSequence(values)),
871                SegmentType::AS_CONFED_SEQUENCE => Ok(AsPathSegment::ConfedSequence(values)),
872                SegmentType::AS_CONFED_SET => Ok(AsPathSegment::ConfedSet(values)),
873            }
874        }
875    }
876
877    /// Check if we can serialize an `AsPath` using the simplified format and get the number of
878    /// elements to do so. The ambiguities that could prevent us from doing so are confederation
879    /// segments and adjacent sequence segments.
880    fn simplified_format_len(segments: &[AsPathSegment]) -> Option<usize> {
881        let mut elements = 0;
882        let mut prev_was_sequence = false;
883        for segment in segments {
884            match segment {
885                AsPathSegment::AsSequence(seq) if !prev_was_sequence => {
886                    prev_was_sequence = true;
887                    elements += seq.len();
888                }
889                AsPathSegment::AsSet(_) => {
890                    prev_was_sequence = false;
891                    elements += 1;
892                }
893                _ => return None,
894            }
895        }
896
897        Some(elements)
898    }
899
900    /// # Serialization format
901    /// For the sake of readability and ease of use within other applications, there are verbose and
902    /// simplified variants for serialization.
903    ///
904    /// ## Simplified format
905    /// The simplified format is the default preferred serialization format. This format does not
906    /// cover confederation segments and involves a single list of ASNs within the path sequence.
907    /// For sets, a list of set members is used in place of an ASN.
908    /// ```rust
909    /// # use bgpkit_parser::models::{Asn, AsPath};
910    /// # use bgpkit_parser::models::AsPathSegment::*;
911    ///
912    /// let a: AsPath = serde_json::from_str("[123, 942, 102]").unwrap();
913    /// let b: AsPath = serde_json::from_str("[231, 432, [643, 836], 352]").unwrap();
914    ///
915    /// assert_eq!(&a.segments[..], &[
916    ///     AsSequence(vec![Asn::from(123), Asn::from(942), Asn::from(102)].into())
917    /// ][..]);
918    /// assert_eq!(&b.segments[..], &[
919    ///     AsSequence(vec![Asn::from(231), Asn::from(432)].into()),
920    ///     AsSet(vec![Asn::from(643), Asn::from(836)].into()),
921    ///     AsSequence(vec![Asn::from(352)].into())
922    /// ][..]);
923    /// ```
924    ///
925    /// ## Verbose format
926    /// The verbose format serves as the fallback format for when the simplified format can not be
927    /// used due to ambiguity. This happens when confederation segments are present, or multiple
928    /// sequences occur back to back. In this format, segments are explicitly seperated and labeled.
929    /// Segment types, denoted by the `ty` field, correspond to the names used within RFC3065
930    /// (`AS_SET`, `AS_SEQUENCE`, `AS_CONFED_SEQUENCE`, `AS_CONFED_SET`).
931    /// ```rust
932    /// # use bgpkit_parser::models::{Asn, AsPath};
933    /// # use bgpkit_parser::models::AsPathSegment::*;
934    ///
935    /// let a = r#"[
936    ///     { "ty": "AS_CONFED_SEQUENCE", "values": [123, 942] },
937    ///     { "ty": "AS_SEQUENCE", "values": [773] },
938    ///     { "ty": "AS_SEQUENCE", "values": [382, 293] }
939    /// ]"#;
940    ///
941    /// let parsed: AsPath = serde_json::from_str(a).unwrap();
942    /// assert_eq!(&parsed.segments[..], &[
943    ///     ConfedSequence(vec![Asn::from(123), Asn::from(942)].into()),
944    ///     AsSequence(vec![Asn::from(773)].into()),
945    ///     AsSequence(vec![Asn::from(382), Asn::from(293)].into())
946    /// ][..]);
947    /// ```
948    impl Serialize for AsPath {
949        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
950        where
951            S: Serializer,
952        {
953            if let Some(num_elements) = simplified_format_len(&self.segments) {
954                // Serialize simplified format
955                let mut seq_serializer = serializer.serialize_seq(Some(num_elements))?;
956
957                for segment in &self.segments {
958                    match segment {
959                        AsPathSegment::AsSequence(elements) => {
960                            elements
961                                .iter()
962                                .try_for_each(|x| seq_serializer.serialize_element(x))?;
963                        }
964                        AsPathSegment::AsSet(x) => seq_serializer.serialize_element(x)?,
965                        _ => unreachable!("simplified_format_len checked for confed segments"),
966                    }
967                }
968                return seq_serializer.end();
969            }
970
971            // Serialize verbose format
972            serializer.collect_seq(&self.segments)
973        }
974    }
975
976    struct AsPathVisitor;
977
978    impl<'de> Visitor<'de> for AsPathVisitor {
979        type Value = AsPath;
980
981        fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
982            formatter.write_str("list of AS_PATH segments")
983        }
984
985        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
986        where
987            A: SeqAccess<'de>,
988        {
989            // Technically, we can handle an input that mixes the simplified and verbose formats,
990            // but we do not want to document this behavior as it may change in future updates.
991            #[derive(Deserialize)]
992            #[serde(untagged)]
993            enum PathElement {
994                SequenceElement(Asn),
995                Set(Vec<Asn>),
996                Verbose(AsPathSegment),
997            }
998
999            let mut append_new_sequence = false;
1000            let mut segments: SmallVec<[AsPathSegment; 1]> = SmallVec::new();
1001            while let Some(element) = seq.next_element()? {
1002                match element {
1003                    PathElement::SequenceElement(x) => {
1004                        if append_new_sequence {
1005                            // If the input is mixed between verbose and regular segments, this flag
1006                            // is used to prevent appending to a verbose sequence.
1007                            append_new_sequence = false;
1008                            segments.push(AsPathSegment::AsSequence(SmallVec::new()));
1009                        }
1010
1011                        if let Some(AsPathSegment::AsSequence(last_sequence)) = segments.last_mut()
1012                        {
1013                            last_sequence.push(x);
1014                        } else {
1015                            let mut new_seq: SmallVec<[Asn; 6]> = SmallVec::new();
1016                            new_seq.push(x);
1017                            segments.push(AsPathSegment::AsSequence(new_seq));
1018                        }
1019                    }
1020                    PathElement::Set(values) => {
1021                        segments.push(AsPathSegment::AsSet(values.into()));
1022                    }
1023                    PathElement::Verbose(verbose) => {
1024                        segments.push(verbose);
1025                    }
1026                }
1027            }
1028
1029            Ok(AsPath { segments })
1030        }
1031    }
1032
1033    impl<'de> Deserialize<'de> for AsPath {
1034        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1035        where
1036            D: Deserializer<'de>,
1037        {
1038            deserializer.deserialize_seq(AsPathVisitor)
1039        }
1040    }
1041}
1042
1043#[cfg(test)]
1044mod tests {
1045    use crate::models::*;
1046    use itertools::Itertools;
1047    use std::collections::HashSet;
1048
1049    #[test]
1050    fn test_aspath_as4path_merge() {
1051        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
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, 3, 7]));
1055
1056        let aspath = AsPath::from_sequence([1, 2]);
1057        let as4path = AsPath::from_sequence([2, 3, 7]);
1058        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1059        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2]));
1060
1061        // when the sequence length is the same, the as4path should be used
1062        let aspath = AsPath::from_sequence([1, 2]);
1063        let as4path = AsPath::from_sequence([3, 4]);
1064        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1065        assert_eq!(newpath.segments[0], AsPathSegment::sequence([3, 4]));
1066
1067        let aspath = AsPath::from_segments(vec![
1068            AsPathSegment::sequence([1, 2, 3, 5]),
1069            AsPathSegment::set([7, 8]),
1070        ]);
1071        let as4path = AsPath::from_sequence([6, 7, 8]);
1072        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1073        assert_eq!(newpath.segments.len(), 2);
1074        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 6, 7, 8]));
1075        assert_eq!(newpath.segments[1], AsPathSegment::set([7, 8]));
1076
1077        let aspath = AsPath::from_segments(vec![
1078            AsPathSegment::sequence([1, 2]),
1079            AsPathSegment::sequence([3, 5]),
1080            AsPathSegment::set([13, 14]),
1081        ]);
1082        let as4path = AsPath::from_segments(vec![
1083            AsPathSegment::sequence([8, 4, 6]),
1084            AsPathSegment::set([11, 12]),
1085        ]);
1086        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1087        assert_eq!(newpath.segments.len(), 3);
1088        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2]));
1089        assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
1090        assert_eq!(newpath.segments[2], AsPathSegment::set([13, 14]));
1091
1092        let aspath = AsPath::from_segments(vec![
1093            AsPathSegment::sequence([1, 2, 3]),
1094            AsPathSegment::sequence([5]),
1095            AsPathSegment::set([13, 14]),
1096        ]);
1097        let as4path = AsPath::from_segments(vec![
1098            AsPathSegment::sequence([7, 8]),
1099            AsPathSegment::set([11, 12]),
1100        ]);
1101        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1102        assert_eq!(newpath.segments.len(), 3);
1103        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 7, 8]));
1104        assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
1105        assert_eq!(newpath.segments[2], AsPathSegment::set([13, 14]));
1106    }
1107
1108    #[test]
1109    fn test_get_origin() {
1110        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1111        let origin = aspath.get_origin_opt();
1112        assert_eq!(origin.unwrap(), 5);
1113
1114        let aspath = AsPath::from_segments(vec![AsPathSegment::set([1, 2, 3, 5])]);
1115        let origin = aspath.get_origin_opt();
1116        assert!(origin.is_none());
1117
1118        let aspath = AsPath::from_segments(vec![AsPathSegment::set([1])]);
1119        let origin = aspath.get_origin_opt();
1120        assert_eq!(origin.unwrap(), 1);
1121
1122        let aspath = AsPath::from_segments(vec![
1123            AsPathSegment::sequence([1, 2, 3, 5]),
1124            AsPathSegment::set([7, 8]),
1125        ]);
1126        let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
1127        assert_eq!(origins, vec![7, 8]);
1128
1129        let aspath = AsPath::from_segments(vec![
1130            AsPathSegment::sequence([1, 2, 3, 5]),
1131            AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)].into()),
1132        ]);
1133        let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
1134        assert_eq!(origins, Vec::<u32>::new());
1135    }
1136
1137    #[test]
1138    fn test_get_collector() {
1139        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1140        let collector = aspath.get_collector_opt();
1141        assert_eq!(collector.unwrap(), 1);
1142
1143        let aspath = AsPath::from_segments(vec![AsPathSegment::set([7])]);
1144        let collector = aspath.get_collector_opt();
1145        assert_eq!(collector.unwrap(), 7);
1146
1147        let aspath = AsPath::from_segments(vec![AsPathSegment::set([7, 8])]);
1148        let collector = aspath.get_collector_opt();
1149        assert!(collector.is_none());
1150    }
1151
1152    #[test]
1153    fn test_aspath_route_iter() {
1154        let path = AsPath::from_segments(vec![AsPathSegment::sequence([3, 4])]);
1155        let mut routes = HashSet::new();
1156        for route in &path {
1157            assert!(routes.insert(route));
1158        }
1159        assert_eq!(1, routes.len());
1160
1161        let path = AsPath::from_segments(vec![
1162            AsPathSegment::set([3, 4]),
1163            AsPathSegment::set([5, 6]),
1164            AsPathSegment::sequence([7, 8]),
1165            AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)].into()),
1166            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(9)].into()),
1167        ]);
1168        assert_eq!(path.route_len(), 4);
1169
1170        let mut routes = HashSet::new();
1171        for route in &path {
1172            assert!(routes.insert(route));
1173        }
1174
1175        assert_eq!(routes.len(), 4);
1176        assert!(routes.contains(&vec![
1177            Asn::from(3),
1178            Asn::from(5),
1179            Asn::from(7),
1180            Asn::from(8)
1181        ]));
1182        assert!(routes.contains(&vec![
1183            Asn::from(3),
1184            Asn::from(6),
1185            Asn::from(7),
1186            Asn::from(8)
1187        ]));
1188        assert!(routes.contains(&vec![
1189            Asn::from(4),
1190            Asn::from(5),
1191            Asn::from(7),
1192            Asn::from(8)
1193        ]));
1194        assert!(routes.contains(&vec![
1195            Asn::from(4),
1196            Asn::from(6),
1197            Asn::from(7),
1198            Asn::from(8)
1199        ]));
1200    }
1201
1202    #[test]
1203    fn test_segment() {
1204        let path_segment = AsPathSegment::sequence([1, 2, 3, 4]);
1205        assert_eq!(path_segment.len(), 4);
1206
1207        // test iter
1208        let mut iter = path_segment.iter();
1209        assert_eq!(iter.next(), Some(&Asn::new_32bit(1)));
1210        assert_eq!(iter.next(), Some(&Asn::new_32bit(2)));
1211        assert_eq!(iter.next(), Some(&Asn::new_32bit(3)));
1212        assert_eq!(iter.next(), Some(&Asn::new_32bit(4)));
1213        assert_eq!(iter.next(), None);
1214
1215        // test iter_mut
1216        let mut path_segment = AsPathSegment::sequence([1]);
1217        let mut iter_mut = path_segment.iter_mut();
1218        assert_eq!(iter_mut.next(), Some(&mut Asn::new_32bit(1)));
1219        assert_eq!(iter_mut.next(), None);
1220
1221        // test is_confed
1222        assert!(AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)].into()).is_confed());
1223        assert!(AsPathSegment::ConfedSet(vec![Asn::new_32bit(1)].into()).is_confed());
1224    }
1225
1226    #[test]
1227    fn test_coalesce() {
1228        let mut a = AsPath::from_segments(vec![
1229            AsPathSegment::sequence([]),
1230            AsPathSegment::sequence([1, 2]),
1231            AsPathSegment::sequence([]),
1232            AsPathSegment::sequence([2]),
1233            AsPathSegment::set([2]),
1234            AsPathSegment::set([5, 3, 3, 2]),
1235        ]);
1236
1237        let expected = AsPath::from_segments(vec![
1238            AsPathSegment::sequence([1, 2, 2]),
1239            AsPathSegment::set([2]),
1240            AsPathSegment::set([5, 3, 3, 2]),
1241        ]);
1242
1243        a.coalesce();
1244        assert_eq!(a, expected);
1245    }
1246
1247    #[test]
1248    fn test_confed_set_dedup() {
1249        let mut path_segment =
1250            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(1)].into());
1251        path_segment.dedup();
1252        assert_eq!(
1253            path_segment,
1254            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)].into())
1255        );
1256
1257        let mut path_segment = AsPathSegment::ConfedSet(
1258            vec![Asn::new_32bit(1), Asn::new_32bit(2), Asn::new_32bit(2)].into(),
1259        );
1260        path_segment.dedup();
1261        assert_eq!(
1262            path_segment,
1263            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into())
1264        );
1265    }
1266
1267    #[test]
1268    fn test_path_to_u32() {
1269        // regular sequence
1270        let path_segment = AsPathSegment::sequence([1, 2, 3, 3]);
1271        assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1, 2, 3, 3]));
1272        assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1, 2, 3]));
1273
1274        // regular set: should return None
1275        let path_segment = AsPathSegment::set([1, 2, 3, 3]);
1276        assert_eq!(path_segment.to_u32_vec_opt(false), None);
1277        assert_eq!(path_segment.to_u32_vec_opt(true), None);
1278
1279        // singular set: should be converted to singleton vector
1280        let path_segment = AsPathSegment::set([1]);
1281        assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1]));
1282        assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1]));
1283
1284        // combination of a sequence and a few singleton sets, should be merged into a single vector
1285        let as_path = AsPath::from_segments(vec![
1286            AsPathSegment::set([4]),
1287            AsPathSegment::sequence([2, 3, 3]),
1288            AsPathSegment::set([1]),
1289        ]);
1290        assert_eq!(as_path.to_u32_vec_opt(false), Some(vec![4, 2, 3, 3, 1]));
1291        assert_eq!(as_path.to_u32_vec_opt(true), Some(vec![4, 2, 3, 1]));
1292
1293        // should the path containing any non-convertible segments, return None
1294        let as_path = AsPath::from_segments(vec![
1295            AsPathSegment::set([4, 2]),
1296            AsPathSegment::sequence([2, 3, 3]),
1297            AsPathSegment::set([1]),
1298        ]);
1299        assert_eq!(as_path.to_u32_vec_opt(false), None);
1300        assert_eq!(as_path.to_u32_vec_opt(true), None);
1301
1302        // other corner cases
1303
1304        // empty path
1305        let as_path = AsPath::from_segments(vec![]);
1306        assert_eq!(as_path.to_u32_vec_opt(false), None);
1307        assert_eq!(as_path.to_u32_vec_opt(true), None);
1308
1309        // path with federation segments
1310        let as_path = AsPath::from_segments(vec![
1311            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into()),
1312            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(3), Asn::new_32bit(4)].into()),
1313        ]);
1314        assert_eq!(as_path.to_u32_vec_opt(false), None);
1315        assert_eq!(as_path.to_u32_vec_opt(true), None);
1316    }
1317
1318    #[test]
1319    fn test_as_ref() {
1320        let path_segment = AsPathSegment::sequence([1, 2]);
1321        assert_eq!(
1322            path_segment.as_ref(),
1323            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1324        );
1325
1326        let path_segment = AsPathSegment::set([1, 2]);
1327        assert_eq!(
1328            path_segment.as_ref(),
1329            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1330        );
1331
1332        let path_segment =
1333            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into());
1334        assert_eq!(
1335            path_segment.as_ref(),
1336            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1337        );
1338
1339        let path_segment =
1340            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into());
1341        assert_eq!(
1342            path_segment.as_ref(),
1343            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1344        );
1345    }
1346
1347    #[test]
1348    fn test_hashing() {
1349        let path_segment = AsPathSegment::sequence([1, 2]);
1350        let path_segment2 = AsPathSegment::sequence([1, 2]);
1351
1352        let hashset = std::iter::once(path_segment).collect::<HashSet<_>>();
1353        assert!(hashset.contains(&path_segment2));
1354    }
1355
1356    #[test]
1357    fn test_equality() {
1358        let path_segment = AsPathSegment::sequence([1, 2]);
1359        let path_segment2 = AsPathSegment::sequence([1, 2]);
1360
1361        assert_eq!(path_segment, path_segment2);
1362
1363        let path_segment = AsPathSegment::sequence([1, 2]);
1364        let path_segment2 = AsPathSegment::set([1, 2, 3]);
1365        assert_ne!(path_segment, path_segment2);
1366
1367        // test equality of AS path longer than 32 ASNs
1368        let path_segment = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
1369        let path_segment2 = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
1370        assert_eq!(path_segment, path_segment2);
1371    }
1372
1373    #[test]
1374    fn test_as_path_display() {
1375        let path = AsPath::from_segments(vec![
1376            AsPathSegment::sequence([1, 2]),
1377            AsPathSegment::set([3, 4]),
1378            AsPathSegment::sequence([5, 6]),
1379            AsPathSegment::ConfedSet(vec![Asn::new_32bit(7)].into()),
1380            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(8)].into()),
1381        ]);
1382
1383        assert_eq!(path.to_string(), "1 2 {3,4} 5 6 {7} 8");
1384    }
1385}