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/// TypeScript-only marker describing the wire/serde encoding of [`AsPath`]:
397/// a flat array where numbers are AS_SEQUENCE ASNs, nested arrays are AS_SET
398/// members, and confederation segments fall back to `{ ty, values }` objects.
399/// [`AsPath`] itself has a manual serde impl, so it cannot carry the ts-rs
400/// derive; this marker stands in for it in generated bindings.
401#[cfg(feature = "ts-rs")]
402#[derive(ts_rs::TS)]
403#[ts(
404    export,
405    type = "(number | number[] | { ty: \"AS_SET\" | \"AS_SEQUENCE\" | \"AS_CONFED_SEQUENCE\" | \"AS_CONFED_SET\", values: number[] })[]"
406)]
407pub struct AsPathWire;
408
409// Define iterator type aliases. The storage mechanism and by extension the iterator types may
410// change later, but these types should remain consistent.
411pub type SegmentIter<'a> = std::slice::Iter<'a, AsPathSegment>;
412pub type SegmentIterMut<'a> = std::slice::IterMut<'a, AsPathSegment>;
413pub type SegmentIntoIter = smallvec::IntoIter<[AsPathSegment; 1]>;
414
415impl AsPath {
416    pub fn new() -> AsPath {
417        AsPath {
418            segments: SmallVec::new(),
419        }
420    }
421
422    /// Shorthand for creating an `AsPath` consisting of a single `AsSequence` segment.
423    pub fn from_sequence<S: AsRef<[u32]>>(seq: S) -> Self {
424        let segment = AsPathSegment::AsSequence(seq.as_ref().iter().copied().map_into().collect());
425
426        AsPath {
427            segments: SmallVec::from_buf([segment]),
428        }
429    }
430
431    pub fn from_segments<S: Into<SmallVec<[AsPathSegment; 1]>>>(segments: S) -> AsPath {
432        AsPath {
433            segments: segments.into(),
434        }
435    }
436
437    /// Adds a new segment to the end of the path. This will change the origin of the path. No
438    /// validation or merging the segment is performed during this step.
439    pub fn append_segment(&mut self, segment: AsPathSegment) {
440        self.segments.push(segment);
441    }
442
443    /// Check if the path is empty. Note that a non-empty path may have a route length of 0 due to
444    /// empty segments or confederation segments.
445    pub fn is_empty(&self) -> bool {
446        self.segments.is_empty()
447    }
448
449    /// Get the total length of the routes this path represents. For example, if this route
450    /// contained a sequence of 5 ASNs followed by a set of 3 ASNs, the total route length would be
451    /// 6.
452    ///
453    /// Confederation segments do not count towards the total route length. This means it is
454    /// possible to have a non-empty AsPath with a length of 0.
455    pub fn route_len(&self) -> usize {
456        self.segments.iter().map(AsPathSegment::route_len).sum()
457    }
458
459    /// Get the number of segments that make up this path. For the number of ASNs in routes
460    /// represented by this path, use [AsPath::route_len].
461    pub fn len(&self) -> usize {
462        self.segments.len()
463    }
464
465    /// Get the total number of routes this path represents. This function assumes the total number
466    /// of route variations can be represented by a u64.
467    pub fn num_route_variations(&self) -> u64 {
468        let mut variations: u64 = 1;
469
470        for segment in &self.segments {
471            if let AsPathSegment::AsSet(x) = segment {
472                variations *= x.len() as u64;
473            }
474        }
475
476        variations
477    }
478
479    /// Checks if any segments of this [AsPath] contain the following ASN.
480    pub fn contains_asn(&self, x: Asn) -> bool {
481        self.iter_segments().flatten().contains(&x)
482    }
483
484    /// Coalesce this [AsPath] into the minimum number of segments required without changing the
485    /// values along the path. This can be helpful as some BGP servers will prepend additional
486    /// segments without coalescing sequences. For de-duplicating see [AsPath::dedup_coalesce].
487    ///
488    /// Changes applied by this function:
489    ///  - Merge adjacent AS_SEQUENCE segments
490    ///  - Merge adjacent AS_CONFED_SEQUENCE segments
491    ///  - Removing empty AS_SEQUENCE and AS_CONFED_SEQUENCE segments
492    ///
493    /// ```rust
494    /// # use bgpkit_parser::models::{AsPath, AsPathSegment};
495    /// let mut a = AsPath::from_segments(vec![
496    ///     AsPathSegment::sequence([]),
497    ///     AsPathSegment::sequence([1, 2]),
498    ///     AsPathSegment::sequence([]),
499    ///     AsPathSegment::sequence([2]),
500    ///     AsPathSegment::set([2]),
501    ///     AsPathSegment::set([5, 3, 3, 2]),
502    /// ]);
503    ///
504    /// let expected = AsPath::from_segments(vec![
505    ///     AsPathSegment::sequence([1, 2, 2]),
506    ///     AsPathSegment::set([2]),
507    ///     AsPathSegment::set([5, 3, 3, 2]),
508    /// ]);
509    ///
510    /// a.coalesce();
511    /// assert_eq!(a, expected);
512    /// ```
513    /// If there is only one segment, no changes will occur. This function will not attempt to
514    /// deduplicate sequences or alter sets.
515    pub fn coalesce(&mut self) {
516        let mut end_index = 0;
517        let mut scan_index = 1;
518
519        while scan_index < self.segments.len() {
520            let (a, b) = self.segments.split_at_mut(scan_index);
521            if !AsPathSegment::merge_in_place(&mut a[end_index], &mut b[0]) {
522                end_index += 1;
523                self.segments.swap(end_index, scan_index);
524            }
525            scan_index += 1;
526        }
527
528        self.segments.truncate(end_index + 1);
529    }
530
531    /// A more aggressive version of [AsPath::coalesce] which also de-duplicates ASNs within this
532    /// path and converts sets of a single ASN to sequences. Some BGP servers will prepend their own
533    /// ASN multiple times when announcing a path to artificially increase the route length and make
534    /// the route seem less less desirable to peers.This function is best suited for use-cases which
535    /// only care about transitions between ASes along the path.
536    ///
537    /// Changes applied by this function:
538    ///  - Merge adjacent AS_SEQUENCE segments
539    ///  - Merge adjacent AS_CONFED_SEQUENCE segments
540    ///  - Removing empty AS_SEQUENCE and AS_CONFED_SEQUENCE segments
541    ///  - De-duplicate ASNs in AS_SEQUENCE and AS_CONFED_SEQUENCE segments
542    ///  - Sort and de-duplicate ASNs in AS_SET and AS_CONFED_SET segments
543    ///  - Convert AS_SET and AS_CONFED_SET segments with exactly 1 element to sequences
544    ///
545    /// ```rust
546    /// # use bgpkit_parser::models::{AsPath, AsPathSegment};
547    /// let mut a = AsPath::from_segments(vec![
548    ///     AsPathSegment::sequence([1, 2]),
549    ///     AsPathSegment::sequence([]),
550    ///     AsPathSegment::sequence([2]),
551    ///     AsPathSegment::set([2]),
552    ///     AsPathSegment::set([5, 3, 3, 2]),
553    /// ]);
554    ///
555    /// let expected = AsPath::from_segments(vec![
556    ///     AsPathSegment::sequence([1, 2]),
557    ///     AsPathSegment::set([2, 3, 5]),
558    /// ]);
559    ///
560    /// a.dedup_coalesce();
561    /// assert_eq!(a, expected);
562    /// ```
563    pub fn dedup_coalesce(&mut self) {
564        if !self.segments.is_empty() {
565            self.segments[0].dedup();
566        }
567        let mut end_index = 0;
568        let mut scan_index = 1;
569
570        while scan_index < self.segments.len() {
571            let (a, b) = self.segments.split_at_mut(scan_index);
572            if !AsPathSegment::dedup_merge_in_place(&mut a[end_index], &mut b[0]) {
573                end_index += 1;
574                self.segments.swap(end_index, scan_index);
575            }
576            scan_index += 1;
577        }
578
579        self.segments.truncate(end_index + 1);
580    }
581
582    /// Checks if two paths correspond to equivalent routes. Unlike `a == b`, this function will
583    /// ignore duplicate ASNs by comparing the coalesced versions of each path.
584    ///
585    /// This is equivalent to [AsPath::eq] after calling [AsPath::dedup_coalesce] on both paths.
586    pub fn has_equivalent_routing(&self, other: &Self) -> bool {
587        let mut a = self.to_owned();
588        let mut b = other.to_owned();
589
590        a.dedup_coalesce();
591        b.dedup_coalesce();
592
593        a == b
594    }
595
596    /// Get the length of ASN required to store all of the ASNs within this path
597    pub fn required_asn_length(&self) -> AsnLength {
598        self.iter_segments().flatten().map(Asn::required_len).fold(
599            AsnLength::Bits16,
600            |a, b| match (a, b) {
601                (AsnLength::Bits16, AsnLength::Bits16) => AsnLength::Bits16,
602                _ => AsnLength::Bits32,
603            },
604        )
605    }
606
607    pub fn iter_segments(&self) -> SegmentIter<'_> {
608        self.segments.iter()
609    }
610
611    pub fn iter_segments_mut(&mut self) -> SegmentIterMut<'_> {
612        self.segments.iter_mut()
613    }
614
615    pub fn into_segments_iter(self) -> SegmentIntoIter {
616        self.segments.into_iter()
617    }
618
619    /// Gets an iterator over all possible routes this path represents.
620    pub fn iter_routes<D>(&self) -> AsPathRouteIter<'_, D>
621    where
622        D: FromIterator<Asn>,
623    {
624        AsPathRouteIter {
625            path: Cow::Borrowed(&self.segments),
626            route_num: 0,
627            total_routes: self.num_route_variations(),
628            _phantom: PhantomData,
629        }
630    }
631
632    /// Construct AsPath from AS_PATH and AS4_PATH
633    ///
634    /// <https://datatracker.ietf.org/doc/html/rfc6793#section-4.2.3>
635    ///
636    /// ```text
637    ///    If the number of AS numbers in the AS_PATH attribute is less than the
638    ///    number of AS numbers in the AS4_PATH attribute, then the AS4_PATH
639    ///    attribute SHALL be ignored, and the AS_PATH attribute SHALL be taken
640    ///    as the AS path information.
641    ///
642    ///    If the number of AS numbers in the AS_PATH attribute is larger than
643    ///    or equal to the number of AS numbers in the AS4_PATH attribute, then
644    ///    the AS path information SHALL be constructed by taking as many AS
645    ///    numbers and path segments as necessary from the leading part of the
646    ///    AS_PATH attribute, and then prepending them to the AS4_PATH attribute
647    ///    so that the AS path information has a number of AS numbers identical
648    ///    to that of the AS_PATH attribute.  Note that a valid
649    ///    AS_CONFED_SEQUENCE or AS_CONFED_SET path segment SHALL be prepended
650    ///    if it is either the leading path segment or is adjacent to a path
651    ///    segment that is prepended.
652    /// ```
653    pub fn merge_aspath_as4path(aspath: &AsPath, as4path: &AsPath) -> AsPath {
654        if aspath.route_len() < as4path.route_len() {
655            // Per RFC6793, if 2-byte AS path is shorter than 4-byte AS path, ignore 4-byte AS path
656            return aspath.clone();
657        }
658
659        // Keep route_len(AS_PATH) - route_len(AS4_PATH) AS numbers from the
660        // leading part of the AS_PATH and prepend them to the AS4_PATH. The
661        // leading count is trimmed across segment boundaries, not per segment:
662        // whole segments are kept while they fit, and the segment straddling
663        // the boundary is cut after the remaining number of AS numbers.
664        let mut leading = aspath.route_len() - as4path.route_len();
665        let mut new_segs: Vec<AsPathSegment> = vec![];
666        for seg in &aspath.segments {
667            if leading == 0 {
668                break;
669            }
670            match seg.route_len() {
671                // Confederation segments contribute no AS numbers to the
672                // route and stay adjacent to the prepended part
673                // (RFC 6793 §4.2.3); empty segments carry no information.
674                0 => new_segs.push(seg.clone()),
675                n if n <= leading => {
676                    new_segs.push(seg.clone());
677                    leading -= n;
678                }
679                _ => {
680                    // Only an AS_SEQUENCE can straddle the boundary: an AS_SET
681                    // counts as a single route ASN and confederation segments
682                    // as none.
683                    let AsPathSegment::AsSequence(v) = seg else {
684                        unreachable!("only an AS_SEQUENCE can exceed the leading count");
685                    };
686                    new_segs.push(AsPathSegment::AsSequence(
687                        v.iter().take(leading).copied().collect(),
688                    ));
689                    leading = 0;
690                }
691            }
692        }
693        new_segs.extend(as4path.segments.iter().cloned());
694
695        let mut merged = AsPath {
696            segments: new_segs.into(),
697        };
698        merged.coalesce();
699        merged
700    }
701
702    /// Iterate through the originating ASNs of this path. This functionality is provided for
703    /// completeness, but in almost all cases this iterator should only contain a single element.
704    pub fn iter_origins(&self) -> impl '_ + Iterator<Item = Asn> {
705        let origin_slice = match self.segments.last() {
706            Some(AsPathSegment::AsSequence(v)) => v.last().map(std::slice::from_ref).unwrap_or(&[]),
707            Some(AsPathSegment::AsSet(v)) => v.as_ref(),
708            _ => &[],
709        };
710
711        origin_slice.iter().copied()
712    }
713
714    /// This function serves as a alternative to [AsPath::iter_origins] which attempts to make the
715    /// assumption that a path can only have exactly one origin. If a path does not have exactly 1
716    /// origin (such as when empty or ending in a set), then `None` will be returned instead.
717    pub fn get_origin_opt(&self) -> Option<Asn> {
718        match self.segments.last() {
719            Some(AsPathSegment::AsSequence(v)) => v.last().copied(),
720            Some(AsPathSegment::AsSet(v)) if v.len() == 1 => Some(v[0]),
721            _ => None,
722        }
723    }
724
725    /// This function optionally returns the first hop in the AS hop, which is considered as the
726    /// collector ASN of the message.
727    pub fn get_collector_opt(&self) -> Option<Asn> {
728        match self.segments.first() {
729            Some(AsPathSegment::AsSequence(v)) => v.first().copied(),
730            Some(AsPathSegment::AsSet(v)) if v.len() == 1 => Some(v[0]),
731            _ => None,
732        }
733    }
734
735    pub fn to_u32_vec_opt(&self, dedup: bool) -> Option<Vec<u32>> {
736        let mut path = vec![];
737
738        // Iterate over the segments in reverse order
739        for seg in self.segments.iter().rev() {
740            // If a segment cannot be converted to a u32, the path is not a simple sequence of
741            // ASNs, and returning a partial AS path would be misleading.
742            let p = seg.to_u32_vec_opt(dedup)?;
743            // For each segment, reverse the ASN order so `path` is stored reversed.
744            path.extend(p.iter().rev());
745        }
746
747        match path.is_empty() {
748            true => {
749                // empty path is not a valid AS path
750                None
751            }
752            false => {
753                // reverse the order of ASNs to get the original path
754                path.reverse();
755                Some(path)
756            }
757        }
758    }
759}
760
761/// Iterates over all route variations the given `AsPath` represents.
762impl<'a> IntoIterator for &'a AsPath {
763    type Item = Vec<Asn>;
764    type IntoIter = AsPathRouteIter<'a, Vec<Asn>>;
765
766    fn into_iter(self) -> Self::IntoIter {
767        self.iter_routes()
768    }
769}
770
771/// Iterates over all route variations the given `AsPath` represents.
772impl IntoIterator for AsPath {
773    type Item = Vec<Asn>;
774    type IntoIter = AsPathRouteIter<'static, Vec<Asn>>;
775
776    fn into_iter(self) -> Self::IntoIter {
777        AsPathRouteIter {
778            total_routes: self.num_route_variations(),
779            path: Cow::Owned(self.segments.into_vec()),
780            route_num: 0,
781            _phantom: PhantomData,
782        }
783    }
784}
785
786impl Display for AsPath {
787    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
788        for (index, segment) in self.iter_segments().enumerate() {
789            if index != 0 {
790                write!(f, " ")?;
791            }
792
793            match segment {
794                AsPathSegment::AsSequence(v) | AsPathSegment::ConfedSequence(v) => {
795                    let mut asn_iter = v.iter();
796                    if let Some(first_element) = asn_iter.next() {
797                        write!(f, "{first_element}")?;
798
799                        for asn in asn_iter {
800                            write!(f, " {asn}")?;
801                        }
802                    }
803                }
804                AsPathSegment::AsSet(v) | AsPathSegment::ConfedSet(v) => {
805                    write!(f, "{{")?;
806                    let mut asn_iter = v.iter();
807                    if let Some(first_element) = asn_iter.next() {
808                        write!(f, "{first_element}")?;
809
810                        for asn in asn_iter {
811                            write!(f, ",{asn}")?;
812                        }
813                    }
814                    write!(f, "}}")?;
815                }
816            }
817        }
818
819        Ok(())
820    }
821}
822
823#[cfg(feature = "serde")]
824mod serde_impl {
825    use super::*;
826    use serde::de::{SeqAccess, Visitor};
827    use serde::ser::SerializeSeq;
828    use serde::{Deserialize, Deserializer, Serialize, Serializer};
829    use std::borrow::Cow;
830
831    /// Segment type names using names from RFC3065.
832    ///
833    /// <https://datatracker.ietf.org/doc/html/rfc3065#section-5>
834    #[allow(non_camel_case_types)]
835    #[derive(Serialize, Deserialize)]
836    enum SegmentType {
837        AS_SET,
838        AS_SEQUENCE,
839        AS_CONFED_SEQUENCE,
840        AS_CONFED_SET,
841    }
842
843    #[derive(Serialize, Deserialize)]
844    struct VerboseSegment<'s> {
845        ty: SegmentType,
846        values: Cow<'s, [Asn]>,
847    }
848
849    impl Serialize for AsPathSegment {
850        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
851        where
852            S: Serializer,
853        {
854            let (ty, elements) = match self {
855                AsPathSegment::AsSequence(x) => (SegmentType::AS_SEQUENCE, x.as_ref()),
856                AsPathSegment::AsSet(x) => (SegmentType::AS_SET, x.as_ref()),
857                AsPathSegment::ConfedSequence(x) => (SegmentType::AS_CONFED_SEQUENCE, x.as_ref()),
858                AsPathSegment::ConfedSet(x) => (SegmentType::AS_CONFED_SET, x.as_ref()),
859            };
860
861            let verbose = VerboseSegment {
862                ty,
863                values: Cow::Borrowed(elements),
864            };
865
866            verbose.serialize(serializer)
867        }
868    }
869
870    impl<'de> Deserialize<'de> for AsPathSegment {
871        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
872        where
873            D: Deserializer<'de>,
874        {
875            let verbose = VerboseSegment::deserialize(deserializer)?;
876
877            let values: SmallVec<[Asn; 6]> = verbose.values.into_owned().into();
878            match verbose.ty {
879                SegmentType::AS_SET => Ok(AsPathSegment::AsSet(values)),
880                SegmentType::AS_SEQUENCE => Ok(AsPathSegment::AsSequence(values)),
881                SegmentType::AS_CONFED_SEQUENCE => Ok(AsPathSegment::ConfedSequence(values)),
882                SegmentType::AS_CONFED_SET => Ok(AsPathSegment::ConfedSet(values)),
883            }
884        }
885    }
886
887    /// Check if we can serialize an `AsPath` using the simplified format and get the number of
888    /// elements to do so. The ambiguities that could prevent us from doing so are confederation
889    /// segments and adjacent sequence segments.
890    fn simplified_format_len(segments: &[AsPathSegment]) -> Option<usize> {
891        let mut elements = 0;
892        let mut prev_was_sequence = false;
893        for segment in segments {
894            match segment {
895                AsPathSegment::AsSequence(seq) if !prev_was_sequence => {
896                    prev_was_sequence = true;
897                    elements += seq.len();
898                }
899                AsPathSegment::AsSet(_) => {
900                    prev_was_sequence = false;
901                    elements += 1;
902                }
903                _ => return None,
904            }
905        }
906
907        Some(elements)
908    }
909
910    /// # Serialization format
911    /// For the sake of readability and ease of use within other applications, there are verbose and
912    /// simplified variants for serialization.
913    ///
914    /// ## Simplified format
915    /// The simplified format is the default preferred serialization format. This format does not
916    /// cover confederation segments and involves a single list of ASNs within the path sequence.
917    /// For sets, a list of set members is used in place of an ASN.
918    /// ```rust
919    /// # use bgpkit_parser::models::{Asn, AsPath};
920    /// # use bgpkit_parser::models::AsPathSegment::*;
921    ///
922    /// let a: AsPath = serde_json::from_str("[123, 942, 102]").unwrap();
923    /// let b: AsPath = serde_json::from_str("[231, 432, [643, 836], 352]").unwrap();
924    ///
925    /// assert_eq!(&a.segments[..], &[
926    ///     AsSequence(vec![Asn::from(123), Asn::from(942), Asn::from(102)].into())
927    /// ][..]);
928    /// assert_eq!(&b.segments[..], &[
929    ///     AsSequence(vec![Asn::from(231), Asn::from(432)].into()),
930    ///     AsSet(vec![Asn::from(643), Asn::from(836)].into()),
931    ///     AsSequence(vec![Asn::from(352)].into())
932    /// ][..]);
933    /// ```
934    ///
935    /// ## Verbose format
936    /// The verbose format serves as the fallback format for when the simplified format can not be
937    /// used due to ambiguity. This happens when confederation segments are present, or multiple
938    /// sequences occur back to back. In this format, segments are explicitly seperated and labeled.
939    /// Segment types, denoted by the `ty` field, correspond to the names used within RFC3065
940    /// (`AS_SET`, `AS_SEQUENCE`, `AS_CONFED_SEQUENCE`, `AS_CONFED_SET`).
941    /// ```rust
942    /// # use bgpkit_parser::models::{Asn, AsPath};
943    /// # use bgpkit_parser::models::AsPathSegment::*;
944    ///
945    /// let a = r#"[
946    ///     { "ty": "AS_CONFED_SEQUENCE", "values": [123, 942] },
947    ///     { "ty": "AS_SEQUENCE", "values": [773] },
948    ///     { "ty": "AS_SEQUENCE", "values": [382, 293] }
949    /// ]"#;
950    ///
951    /// let parsed: AsPath = serde_json::from_str(a).unwrap();
952    /// assert_eq!(&parsed.segments[..], &[
953    ///     ConfedSequence(vec![Asn::from(123), Asn::from(942)].into()),
954    ///     AsSequence(vec![Asn::from(773)].into()),
955    ///     AsSequence(vec![Asn::from(382), Asn::from(293)].into())
956    /// ][..]);
957    /// ```
958    impl Serialize for AsPath {
959        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
960        where
961            S: Serializer,
962        {
963            if let Some(num_elements) = simplified_format_len(&self.segments) {
964                // Serialize simplified format
965                let mut seq_serializer = serializer.serialize_seq(Some(num_elements))?;
966
967                for segment in &self.segments {
968                    match segment {
969                        AsPathSegment::AsSequence(elements) => {
970                            elements
971                                .iter()
972                                .try_for_each(|x| seq_serializer.serialize_element(x))?;
973                        }
974                        AsPathSegment::AsSet(x) => seq_serializer.serialize_element(x)?,
975                        _ => unreachable!("simplified_format_len checked for confed segments"),
976                    }
977                }
978                return seq_serializer.end();
979            }
980
981            // Serialize verbose format
982            serializer.collect_seq(&self.segments)
983        }
984    }
985
986    struct AsPathVisitor;
987
988    impl<'de> Visitor<'de> for AsPathVisitor {
989        type Value = AsPath;
990
991        fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
992            formatter.write_str("list of AS_PATH segments")
993        }
994
995        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
996        where
997            A: SeqAccess<'de>,
998        {
999            // Technically, we can handle an input that mixes the simplified and verbose formats,
1000            // but we do not want to document this behavior as it may change in future updates.
1001            #[derive(Deserialize)]
1002            #[serde(untagged)]
1003            enum PathElement {
1004                SequenceElement(Asn),
1005                Set(Vec<Asn>),
1006                Verbose(AsPathSegment),
1007            }
1008
1009            let mut append_new_sequence = false;
1010            let mut segments: SmallVec<[AsPathSegment; 1]> = SmallVec::new();
1011            while let Some(element) = seq.next_element()? {
1012                match element {
1013                    PathElement::SequenceElement(x) => {
1014                        if append_new_sequence {
1015                            // If the input is mixed between verbose and regular segments, this flag
1016                            // is used to prevent appending to a verbose sequence.
1017                            append_new_sequence = false;
1018                            segments.push(AsPathSegment::AsSequence(SmallVec::new()));
1019                        }
1020
1021                        if let Some(AsPathSegment::AsSequence(last_sequence)) = segments.last_mut()
1022                        {
1023                            last_sequence.push(x);
1024                        } else {
1025                            let mut new_seq: SmallVec<[Asn; 6]> = SmallVec::new();
1026                            new_seq.push(x);
1027                            segments.push(AsPathSegment::AsSequence(new_seq));
1028                        }
1029                    }
1030                    PathElement::Set(values) => {
1031                        segments.push(AsPathSegment::AsSet(values.into()));
1032                    }
1033                    PathElement::Verbose(verbose) => {
1034                        segments.push(verbose);
1035                    }
1036                }
1037            }
1038
1039            Ok(AsPath { segments })
1040        }
1041    }
1042
1043    impl<'de> Deserialize<'de> for AsPath {
1044        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1045        where
1046            D: Deserializer<'de>,
1047        {
1048            deserializer.deserialize_seq(AsPathVisitor)
1049        }
1050    }
1051}
1052
1053#[cfg(test)]
1054mod tests {
1055    use crate::models::*;
1056    use itertools::Itertools;
1057    use std::collections::HashSet;
1058
1059    #[test]
1060    fn test_aspath_as4path_merge() {
1061        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1062        let as4path = AsPath::from_sequence([2, 3, 7]);
1063        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1064        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 3, 7]));
1065
1066        let aspath = AsPath::from_sequence([1, 2]);
1067        let as4path = AsPath::from_sequence([2, 3, 7]);
1068        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1069        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2]));
1070
1071        // when the sequence length is the same, the as4path should be used
1072        let aspath = AsPath::from_sequence([1, 2]);
1073        let as4path = AsPath::from_sequence([3, 4]);
1074        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1075        assert_eq!(newpath.segments[0], AsPathSegment::sequence([3, 4]));
1076
1077        let aspath = AsPath::from_segments(vec![
1078            AsPathSegment::sequence([1, 2, 3, 5]),
1079            AsPathSegment::set([7, 8]),
1080        ]);
1081        let as4path = AsPath::from_sequence([6, 7, 8]);
1082        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1083        // route_len 5 - 3 = 2 leading AS numbers, then the whole AS4_PATH;
1084        // the AS_PATH trailing set is the stale 2-octet mirror and is dropped.
1085        assert_eq!(newpath.segments.len(), 1);
1086        assert_eq!(
1087            newpath.segments[0],
1088            AsPathSegment::sequence([1, 2, 6, 7, 8])
1089        );
1090
1091        let aspath = AsPath::from_segments(vec![
1092            AsPathSegment::sequence([1, 2]),
1093            AsPathSegment::sequence([3, 5]),
1094            AsPathSegment::set([13, 14]),
1095        ]);
1096        let as4path = AsPath::from_segments(vec![
1097            AsPathSegment::sequence([8, 4, 6]),
1098            AsPathSegment::set([11, 12]),
1099        ]);
1100        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1101        // route_len 5 - 4 = 1 leading AS number cut across segment boundaries.
1102        assert_eq!(newpath.segments.len(), 2);
1103        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 8, 4, 6]));
1104        assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
1105
1106        let aspath = AsPath::from_segments(vec![
1107            AsPathSegment::sequence([1, 2, 3]),
1108            AsPathSegment::sequence([5]),
1109            AsPathSegment::set([13, 14]),
1110        ]);
1111        let as4path = AsPath::from_segments(vec![
1112            AsPathSegment::sequence([7, 8]),
1113            AsPathSegment::set([11, 12]),
1114        ]);
1115        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1116        // route_len 5 - 3 = 2 leading AS numbers cut across segment boundaries.
1117        assert_eq!(newpath.segments.len(), 2);
1118        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 7, 8]));
1119        assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
1120
1121        // Misaligned segment boundaries: the leading count must be trimmed
1122        // across segments, not aligned per segment pair (PR #330 review).
1123        let aspath = AsPath::from_segments(vec![
1124            AsPathSegment::sequence([1, 2]),
1125            AsPathSegment::sequence([3, 4]),
1126        ]);
1127        let as4path = AsPath::from_sequence([9, 10]);
1128        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1129        assert_eq!(newpath.segments.len(), 1);
1130        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 9, 10]));
1131    }
1132
1133    #[test]
1134    fn test_get_origin() {
1135        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1136        let origin = aspath.get_origin_opt();
1137        assert_eq!(origin.unwrap(), 5);
1138
1139        let aspath = AsPath::from_segments(vec![AsPathSegment::set([1, 2, 3, 5])]);
1140        let origin = aspath.get_origin_opt();
1141        assert!(origin.is_none());
1142
1143        let aspath = AsPath::from_segments(vec![AsPathSegment::set([1])]);
1144        let origin = aspath.get_origin_opt();
1145        assert_eq!(origin.unwrap(), 1);
1146
1147        let aspath = AsPath::from_segments(vec![
1148            AsPathSegment::sequence([1, 2, 3, 5]),
1149            AsPathSegment::set([7, 8]),
1150        ]);
1151        let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
1152        assert_eq!(origins, vec![7, 8]);
1153
1154        let aspath = AsPath::from_segments(vec![
1155            AsPathSegment::sequence([1, 2, 3, 5]),
1156            AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)].into()),
1157        ]);
1158        let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
1159        assert_eq!(origins, Vec::<u32>::new());
1160    }
1161
1162    #[test]
1163    fn test_get_collector() {
1164        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1165        let collector = aspath.get_collector_opt();
1166        assert_eq!(collector.unwrap(), 1);
1167
1168        let aspath = AsPath::from_segments(vec![AsPathSegment::set([7])]);
1169        let collector = aspath.get_collector_opt();
1170        assert_eq!(collector.unwrap(), 7);
1171
1172        let aspath = AsPath::from_segments(vec![AsPathSegment::set([7, 8])]);
1173        let collector = aspath.get_collector_opt();
1174        assert!(collector.is_none());
1175    }
1176
1177    #[test]
1178    fn test_aspath_route_iter() {
1179        let path = AsPath::from_segments(vec![AsPathSegment::sequence([3, 4])]);
1180        let mut routes = HashSet::new();
1181        for route in &path {
1182            assert!(routes.insert(route));
1183        }
1184        assert_eq!(1, routes.len());
1185
1186        let path = AsPath::from_segments(vec![
1187            AsPathSegment::set([3, 4]),
1188            AsPathSegment::set([5, 6]),
1189            AsPathSegment::sequence([7, 8]),
1190            AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)].into()),
1191            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(9)].into()),
1192        ]);
1193        assert_eq!(path.route_len(), 4);
1194
1195        let mut routes = HashSet::new();
1196        for route in &path {
1197            assert!(routes.insert(route));
1198        }
1199
1200        assert_eq!(routes.len(), 4);
1201        assert!(routes.contains(&vec![
1202            Asn::from(3),
1203            Asn::from(5),
1204            Asn::from(7),
1205            Asn::from(8)
1206        ]));
1207        assert!(routes.contains(&vec![
1208            Asn::from(3),
1209            Asn::from(6),
1210            Asn::from(7),
1211            Asn::from(8)
1212        ]));
1213        assert!(routes.contains(&vec![
1214            Asn::from(4),
1215            Asn::from(5),
1216            Asn::from(7),
1217            Asn::from(8)
1218        ]));
1219        assert!(routes.contains(&vec![
1220            Asn::from(4),
1221            Asn::from(6),
1222            Asn::from(7),
1223            Asn::from(8)
1224        ]));
1225    }
1226
1227    #[test]
1228    fn test_segment() {
1229        let path_segment = AsPathSegment::sequence([1, 2, 3, 4]);
1230        assert_eq!(path_segment.len(), 4);
1231
1232        // test iter
1233        let mut iter = path_segment.iter();
1234        assert_eq!(iter.next(), Some(&Asn::new_32bit(1)));
1235        assert_eq!(iter.next(), Some(&Asn::new_32bit(2)));
1236        assert_eq!(iter.next(), Some(&Asn::new_32bit(3)));
1237        assert_eq!(iter.next(), Some(&Asn::new_32bit(4)));
1238        assert_eq!(iter.next(), None);
1239
1240        // test iter_mut
1241        let mut path_segment = AsPathSegment::sequence([1]);
1242        let mut iter_mut = path_segment.iter_mut();
1243        assert_eq!(iter_mut.next(), Some(&mut Asn::new_32bit(1)));
1244        assert_eq!(iter_mut.next(), None);
1245
1246        // test is_confed
1247        assert!(AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)].into()).is_confed());
1248        assert!(AsPathSegment::ConfedSet(vec![Asn::new_32bit(1)].into()).is_confed());
1249    }
1250
1251    #[test]
1252    fn test_coalesce() {
1253        let mut a = AsPath::from_segments(vec![
1254            AsPathSegment::sequence([]),
1255            AsPathSegment::sequence([1, 2]),
1256            AsPathSegment::sequence([]),
1257            AsPathSegment::sequence([2]),
1258            AsPathSegment::set([2]),
1259            AsPathSegment::set([5, 3, 3, 2]),
1260        ]);
1261
1262        let expected = AsPath::from_segments(vec![
1263            AsPathSegment::sequence([1, 2, 2]),
1264            AsPathSegment::set([2]),
1265            AsPathSegment::set([5, 3, 3, 2]),
1266        ]);
1267
1268        a.coalesce();
1269        assert_eq!(a, expected);
1270    }
1271
1272    #[test]
1273    fn test_confed_set_dedup() {
1274        let mut path_segment =
1275            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(1)].into());
1276        path_segment.dedup();
1277        assert_eq!(
1278            path_segment,
1279            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)].into())
1280        );
1281
1282        let mut path_segment = AsPathSegment::ConfedSet(
1283            vec![Asn::new_32bit(1), Asn::new_32bit(2), Asn::new_32bit(2)].into(),
1284        );
1285        path_segment.dedup();
1286        assert_eq!(
1287            path_segment,
1288            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into())
1289        );
1290    }
1291
1292    #[test]
1293    fn test_path_to_u32() {
1294        // regular sequence
1295        let path_segment = AsPathSegment::sequence([1, 2, 3, 3]);
1296        assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1, 2, 3, 3]));
1297        assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1, 2, 3]));
1298
1299        // regular set: should return None
1300        let path_segment = AsPathSegment::set([1, 2, 3, 3]);
1301        assert_eq!(path_segment.to_u32_vec_opt(false), None);
1302        assert_eq!(path_segment.to_u32_vec_opt(true), None);
1303
1304        // singular set: should be converted to singleton vector
1305        let path_segment = AsPathSegment::set([1]);
1306        assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1]));
1307        assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1]));
1308
1309        // combination of a sequence and a few singleton sets, should be merged into a single vector
1310        let as_path = AsPath::from_segments(vec![
1311            AsPathSegment::set([4]),
1312            AsPathSegment::sequence([2, 3, 3]),
1313            AsPathSegment::set([1]),
1314        ]);
1315        assert_eq!(as_path.to_u32_vec_opt(false), Some(vec![4, 2, 3, 3, 1]));
1316        assert_eq!(as_path.to_u32_vec_opt(true), Some(vec![4, 2, 3, 1]));
1317
1318        // should the path containing any non-convertible segments, return None
1319        let as_path = AsPath::from_segments(vec![
1320            AsPathSegment::set([4, 2]),
1321            AsPathSegment::sequence([2, 3, 3]),
1322            AsPathSegment::set([1]),
1323        ]);
1324        assert_eq!(as_path.to_u32_vec_opt(false), None);
1325        assert_eq!(as_path.to_u32_vec_opt(true), None);
1326
1327        // other corner cases
1328
1329        // empty path
1330        let as_path = AsPath::from_segments(vec![]);
1331        assert_eq!(as_path.to_u32_vec_opt(false), None);
1332        assert_eq!(as_path.to_u32_vec_opt(true), None);
1333
1334        // path with federation segments
1335        let as_path = AsPath::from_segments(vec![
1336            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into()),
1337            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(3), Asn::new_32bit(4)].into()),
1338        ]);
1339        assert_eq!(as_path.to_u32_vec_opt(false), None);
1340        assert_eq!(as_path.to_u32_vec_opt(true), None);
1341    }
1342
1343    #[test]
1344    fn test_as_ref() {
1345        let path_segment = AsPathSegment::sequence([1, 2]);
1346        assert_eq!(
1347            path_segment.as_ref(),
1348            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1349        );
1350
1351        let path_segment = AsPathSegment::set([1, 2]);
1352        assert_eq!(
1353            path_segment.as_ref(),
1354            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1355        );
1356
1357        let path_segment =
1358            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into());
1359        assert_eq!(
1360            path_segment.as_ref(),
1361            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1362        );
1363
1364        let path_segment =
1365            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into());
1366        assert_eq!(
1367            path_segment.as_ref(),
1368            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1369        );
1370    }
1371
1372    #[test]
1373    fn test_hashing() {
1374        let path_segment = AsPathSegment::sequence([1, 2]);
1375        let path_segment2 = AsPathSegment::sequence([1, 2]);
1376
1377        let hashset = std::iter::once(path_segment).collect::<HashSet<_>>();
1378        assert!(hashset.contains(&path_segment2));
1379    }
1380
1381    #[test]
1382    fn test_equality() {
1383        let path_segment = AsPathSegment::sequence([1, 2]);
1384        let path_segment2 = AsPathSegment::sequence([1, 2]);
1385
1386        assert_eq!(path_segment, path_segment2);
1387
1388        let path_segment = AsPathSegment::sequence([1, 2]);
1389        let path_segment2 = AsPathSegment::set([1, 2, 3]);
1390        assert_ne!(path_segment, path_segment2);
1391
1392        // test equality of AS path longer than 32 ASNs
1393        let path_segment = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
1394        let path_segment2 = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
1395        assert_eq!(path_segment, path_segment2);
1396    }
1397
1398    #[test]
1399    fn test_as_path_display() {
1400        let path = AsPath::from_segments(vec![
1401            AsPathSegment::sequence([1, 2]),
1402            AsPathSegment::set([3, 4]),
1403            AsPathSegment::sequence([5, 6]),
1404            AsPathSegment::ConfedSet(vec![Asn::new_32bit(7)].into()),
1405            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(8)].into()),
1406        ]);
1407
1408        assert_eq!(path.to_string(), "1 2 {3,4} 5 6 {7} 8");
1409    }
1410}