Skip to main content

bgpkit_parser/parser/iters/
mod.rs

1/*!
2Iterator implementations for bgpkit-parser.
3
4This module contains different iterator implementations for parsing BGP data:
5- `default`: Standard iterators that skip errors (RecordIterator, ElemIterator)
6- `fallible`: Fallible iterators that return Results (FallibleRecordIterator, FallibleElemIterator)
7- `update`: Iterators for BGP UPDATE messages (UpdateIterator, FallibleUpdateIterator)
8
9It also contains the trait implementations that enable BgpkitParser to be used with
10Rust's iterator syntax.
11*/
12
13pub mod default;
14pub mod fallible;
15mod raw;
16mod route;
17mod update;
18
19// Re-export all iterator types for convenience
20pub use default::{ElemIterator, RecordIterator};
21pub use fallible::{FallibleElemIterator, FallibleRecordIterator};
22pub use raw::RawRecordIterator;
23pub use route::{FallibleRouteIterator, RouteIterator};
24pub use update::{
25    Bgp4MpUpdate, FallibleUpdateIterator, MrtUpdate, TableDumpV2Entry, UpdateIterator,
26};
27
28use crate::models::BgpElem;
29use crate::models::{MrtMessage, MrtRecord, TableDumpV2Message};
30use crate::parser::BgpkitParser;
31use crate::Elementor;
32use crate::RawMrtRecord;
33use std::io::Read;
34use std::path::Path;
35
36pub(crate) fn write_mrt_core_dump(enabled: bool, bytes: Option<Vec<u8>>) {
37    write_mrt_core_dump_to_path(enabled, bytes, "mrt_core_dump");
38}
39
40pub(crate) fn write_mrt_core_dump_to_path<P: AsRef<Path>>(
41    enabled: bool,
42    bytes: Option<Vec<u8>>,
43    path: P,
44) {
45    if enabled {
46        if let Some(bytes) = bytes {
47            std::fs::write(path, bytes).expect("Unable to write to mrt_core_dump");
48        }
49    }
50}
51
52/// Use [ElemIterator] as the default iterator to return [BgpElem]s instead of [MrtRecord]s.
53impl<R: Read> IntoIterator for BgpkitParser<R> {
54    type Item = BgpElem;
55    type IntoIter = ElemIterator<R>;
56
57    fn into_iter(self) -> Self::IntoIter {
58        ElemIterator::new(self)
59    }
60}
61
62impl<R> BgpkitParser<R> {
63    pub fn into_record_iter(self) -> RecordIterator<R> {
64        RecordIterator::new(self)
65    }
66
67    pub fn into_elem_iter(self) -> ElemIterator<R> {
68        ElemIterator::new(self)
69    }
70
71    pub fn into_raw_record_iter(self) -> RawRecordIterator<R> {
72        RawRecordIterator::new(self)
73    }
74
75    /// Creates an iterator over BGP announcements from MRT data.
76    ///
77    /// This iterator yields `MrtUpdate` items from both UPDATES files (BGP4MP messages)
78    /// and RIB dump files (TableDump/TableDumpV2 messages). It's a middle ground
79    /// between `into_record_iter()` and `into_elem_iter()`:
80    ///
81    /// - More focused than `into_record_iter()` as it only returns BGP announcements
82    /// - More efficient than `into_elem_iter()` as it doesn't duplicate attributes per prefix
83    ///
84    /// The iterator returns an `MrtUpdate` enum with variants:
85    /// - `Bgp4MpUpdate`: BGP UPDATE messages from UPDATES files
86    /// - `TableDumpV2Entry`: RIB entries from TableDumpV2 RIB dumps
87    /// - `TableDumpMessage`: Legacy TableDump v1 messages
88    ///
89    /// # Example
90    /// ```no_run
91    /// use bgpkit_parser::{BgpkitParser, MrtUpdate};
92    ///
93    /// let parser = BgpkitParser::new("updates.mrt").unwrap();
94    /// for update in parser.into_update_iter() {
95    ///     match update {
96    ///         MrtUpdate::Bgp4MpUpdate(u) => {
97    ///             println!("Peer {} announced {} prefixes",
98    ///                 u.peer_ip,
99    ///                 u.message.announced_prefixes.len()
100    ///             );
101    ///         }
102    ///         MrtUpdate::TableDumpV2Entry(e) => {
103    ///             println!("RIB entry for {} with {} peers",
104    ///                 e.prefix,
105    ///                 e.rib_entries.len()
106    ///             );
107    ///         }
108    ///         MrtUpdate::TableDumpMessage(m) => {
109    ///             println!("Legacy table dump for {}", m.prefix);
110    ///         }
111    ///     }
112    /// }
113    /// ```
114    pub fn into_update_iter(self) -> UpdateIterator<R> {
115        UpdateIterator::new(self)
116    }
117
118    /// Creates an iterator over lightweight route elements from MRT data.
119    ///
120    /// This iterator yields [`BgpRouteElem`](crate::models::BgpRouteElem)
121    /// values and only parses route identity, peer metadata, timestamp, and
122    /// AS path. Use [`into_elem_iter`](Self::into_elem_iter) when you need
123    /// the full [`BgpElem`] attribute set. Filters that only depend on route
124    /// fields are supported; `community` filters do not match route elements.
125    pub fn into_route_iter(self) -> RouteIterator<R> {
126        RouteIterator::new(self)
127    }
128
129    /// Creates a fallible iterator over MRT records that returns parsing errors.
130    ///
131    /// # Example
132    /// ```no_run
133    /// use bgpkit_parser::BgpkitParser;
134    ///
135    /// let parser = BgpkitParser::new("updates.mrt").unwrap();
136    /// for result in parser.into_fallible_record_iter() {
137    ///     match result {
138    ///         Ok(record) => {
139    ///             // Process the record
140    ///         }
141    ///         Err(e) => {
142    ///             // Handle the error
143    ///             eprintln!("Error parsing record: {}", e);
144    ///         }
145    ///     }
146    /// }
147    /// ```
148    pub fn into_fallible_record_iter(self) -> FallibleRecordIterator<R> {
149        FallibleRecordIterator::new(self)
150    }
151
152    /// Creates a fallible iterator over BGP elements that returns parsing errors.
153    ///
154    /// # Example
155    /// ```no_run
156    /// use bgpkit_parser::BgpkitParser;
157    ///
158    /// let parser = BgpkitParser::new("updates.mrt").unwrap();
159    /// for result in parser.into_fallible_elem_iter() {
160    ///     match result {
161    ///         Ok(elem) => {
162    ///             // Process the element
163    ///         }
164    ///         Err(e) => {
165    ///             // Handle the error
166    ///             eprintln!("Error parsing element: {}", e);
167    ///         }
168    ///     }
169    /// }
170    /// ```
171    pub fn into_fallible_elem_iter(self) -> FallibleElemIterator<R> {
172        FallibleElemIterator::new(self)
173    }
174
175    /// Creates a fallible iterator over BGP announcements that returns parsing errors.
176    ///
177    /// Unlike the default `into_update_iter()`, this iterator returns
178    /// `Result<MrtUpdate, ParserErrorWithBytes>` allowing users to handle parsing
179    /// errors explicitly instead of having them logged and skipped.
180    ///
181    /// # Example
182    /// ```no_run
183    /// use bgpkit_parser::{BgpkitParser, MrtUpdate};
184    ///
185    /// let parser = BgpkitParser::new("updates.mrt").unwrap();
186    /// for result in parser.into_fallible_update_iter() {
187    ///     match result {
188    ///         Ok(MrtUpdate::Bgp4MpUpdate(update)) => {
189    ///             println!("Peer {} announced {} prefixes",
190    ///                 update.peer_ip,
191    ///                 update.message.announced_prefixes.len()
192    ///             );
193    ///         }
194    ///         Ok(_) => { /* handle other variants */ }
195    ///         Err(e) => {
196    ///             eprintln!("Error parsing: {}", e);
197    ///         }
198    ///     }
199    /// }
200    /// ```
201    pub fn into_fallible_update_iter(self) -> FallibleUpdateIterator<R> {
202        FallibleUpdateIterator::new(self)
203    }
204
205    /// Creates a fallible iterator over lightweight route elements.
206    pub fn into_fallible_route_iter(self) -> FallibleRouteIterator<R> {
207        FallibleRouteIterator::new(self)
208    }
209
210    /// Creates an Elementor pre-initialized with PeerIndexTable and an iterator over raw records.
211    ///
212    /// This is useful for parallel processing where the Elementor needs to be shared across threads.
213    /// The Elementor is created with the PeerIndexTable from the first record if present,
214    /// otherwise a new Elementor is created.
215    ///
216    /// # Example
217    /// See the `parallel_records_to_elem` example for full usage.
218    /// ```ignore
219    /// use bgpkit_parser::BgpkitParser;
220    ///
221    /// let parser = BgpkitParser::new_cached(url, "/tmp")?;
222    /// let (elementor, records) = parser.into_elementor_and_raw_record_iter();
223    /// ```
224    ///
225    pub fn into_elementor_and_raw_record_iter(
226        self,
227    ) -> (Elementor, impl Iterator<Item = RawMrtRecord>)
228    where
229        R: Read,
230    {
231        let mut raw_iter = RawRecordIterator::new(self).peekable();
232        let elementor = match raw_iter.peek().cloned().and_then(|r| r.parse().ok()) {
233            Some(MrtRecord {
234                message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(pit)),
235                ..
236            }) => {
237                raw_iter.next();
238                Elementor::with_peer_table(pit)
239            }
240            _ => Elementor::new(),
241        };
242        (elementor, raw_iter)
243    }
244
245    /// Creates an Elementor pre-initialized with PeerIndexTable and an iterator over parsed records.
246    ///
247    /// This is useful for parallel processing where the Elementor needs to be shared across threads.
248    /// The Elementor is created with the PeerIndexTable from the first record if present,
249    /// otherwise a new Elementor is created.
250    ///
251    /// # Example
252    /// See the `parallel_records_to_elem` example for full usage.
253    pub fn into_elementor_and_record_iter(self) -> (Elementor, impl Iterator<Item = MrtRecord>)
254    where
255        R: Read,
256    {
257        let mut record_iter = RecordIterator::new(self).peekable();
258        let elementor = match record_iter.peek().cloned() {
259            Some(MrtRecord {
260                message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(pit)),
261                ..
262            }) => {
263                record_iter.next();
264                Elementor::with_peer_table(pit)
265            }
266            _ => Elementor::new(),
267        };
268        (elementor, record_iter)
269    }
270}