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}