bgpkit_parser/parser/iters/
fallible.rs

1/*!
2Fallible iterator implementations that return Results, exposing parsing errors to users.
3
4These iterators complement the default iterators by returning `Result<T, ParserErrorWithBytes>`
5instead of silently skipping errors. This allows users to handle errors explicitly while
6maintaining backward compatibility with existing code.
7*/
8use crate::error::{ParserError, ParserErrorWithBytes};
9use crate::models::*;
10use crate::parser::BgpkitParser;
11use crate::{Elementor, Filterable};
12use std::io::Read;
13
14/// Fallible iterator over MRT records that returns parsing errors.
15///
16/// Unlike the default `RecordIterator`, this iterator returns `Result<MrtRecord, ParserErrorWithBytes>`
17/// allowing users to handle parsing errors explicitly instead of having them logged and skipped.
18pub struct FallibleRecordIterator<R> {
19    parser: BgpkitParser<R>,
20    elementor: Elementor,
21}
22
23impl<R> FallibleRecordIterator<R> {
24    pub(crate) fn new(parser: BgpkitParser<R>) -> Self {
25        FallibleRecordIterator {
26            parser,
27            elementor: Elementor::new(),
28        }
29    }
30}
31
32impl<R: Read> Iterator for FallibleRecordIterator<R> {
33    type Item = Result<MrtRecord, ParserErrorWithBytes>;
34
35    fn next(&mut self) -> Option<Self::Item> {
36        loop {
37            match self.parser.next_record() {
38                Ok(record) => {
39                    // Apply filters if any are set
40                    let filters = &self.parser.filters;
41                    if filters.is_empty() {
42                        return Some(Ok(record));
43                    }
44
45                    // Special handling for PeerIndexTable - always pass through
46                    if let MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(_)) =
47                        &record.message
48                    {
49                        let _ = self.elementor.record_to_elems(record.clone());
50                        return Some(Ok(record));
51                    }
52
53                    // Check if any elements from this record match the filters
54                    let elems = self.elementor.record_to_elems(record.clone());
55                    if elems.iter().any(|e| e.match_filters(filters)) {
56                        return Some(Ok(record));
57                    }
58                    // Record doesn't match filters, continue to next
59                    continue;
60                }
61                Err(e) if matches!(e.error, ParserError::EofExpected) => {
62                    // Normal end of file
63                    return None;
64                }
65                Err(e) => {
66                    // Return the error to the user
67                    return Some(Err(e));
68                }
69            }
70        }
71    }
72}
73
74/// Fallible iterator over BGP elements that returns parsing errors.
75///
76/// Unlike the default `ElemIterator`, this iterator returns `Result<BgpElem, ParserErrorWithBytes>`
77/// for each successfully parsed element, and surfaces any parsing errors encountered.
78pub struct FallibleElemIterator<R> {
79    cache_elems: Vec<BgpElem>,
80    record_iter: FallibleRecordIterator<R>,
81    elementor: Elementor,
82}
83
84impl<R> FallibleElemIterator<R> {
85    pub(crate) fn new(parser: BgpkitParser<R>) -> Self {
86        FallibleElemIterator {
87            record_iter: FallibleRecordIterator::new(parser),
88            cache_elems: vec![],
89            elementor: Elementor::new(),
90        }
91    }
92}
93
94impl<R: Read> Iterator for FallibleElemIterator<R> {
95    type Item = Result<BgpElem, ParserErrorWithBytes>;
96
97    fn next(&mut self) -> Option<Self::Item> {
98        loop {
99            // First check if we have cached elements
100            if !self.cache_elems.is_empty() {
101                if let Some(elem) = self.cache_elems.pop() {
102                    if elem.match_filters(&self.record_iter.parser.filters) {
103                        return Some(Ok(elem));
104                    }
105                    // Element doesn't match filters, continue to next
106                    continue;
107                }
108            }
109
110            // Need to refill cache from next record
111            match self.record_iter.next() {
112                None => return None,
113                Some(Err(e)) => return Some(Err(e)),
114                Some(Ok(record)) => {
115                    let mut elems = self.elementor.record_to_elems(record);
116                    if elems.is_empty() {
117                        // No elements from this record, try next
118                        continue;
119                    }
120                    // Reverse to maintain order when popping
121                    elems.reverse();
122                    self.cache_elems = elems;
123                    continue;
124                }
125            }
126        }
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use std::io::Cursor;
134
135    /// Create a test parser with mock data that will cause parsing errors
136    fn create_test_parser_with_errors() -> BgpkitParser<Cursor<Vec<u8>>> {
137        // Create some invalid MRT data that will trigger parsing errors
138        let invalid_data = vec![
139            // MRT header with invalid type
140            0x00, 0x00, 0x00, 0x00, // timestamp
141            0xFF, 0xFF, // invalid type
142            0x00, 0x00, // subtype
143            0x00, 0x00, 0x00, 0x04, // length
144            0x00, 0x00, 0x00, 0x00, // dummy data
145        ];
146
147        let cursor = Cursor::new(invalid_data);
148        BgpkitParser::from_reader(cursor)
149    }
150
151    /// Create a test parser with valid data
152    fn create_test_parser_with_valid_data() -> BgpkitParser<Cursor<Vec<u8>>> {
153        // This would need actual valid MRT data - for now using empty data
154        // which will result in EOF
155        let cursor = Cursor::new(vec![]);
156        BgpkitParser::from_reader(cursor)
157    }
158
159    #[test]
160    fn test_fallible_record_iterator_with_errors() {
161        let parser = create_test_parser_with_errors();
162        let mut iter = parser.into_fallible_record_iter();
163
164        // First item should be an error
165        let result = iter.next();
166        assert!(result.is_some());
167        assert!(result.unwrap().is_err());
168    }
169
170    #[test]
171    fn test_fallible_record_iterator_eof() {
172        let parser = create_test_parser_with_valid_data();
173        let mut iter = parser.into_fallible_record_iter();
174
175        // Should return None on EOF
176        let result = iter.next();
177        assert!(result.is_none());
178    }
179
180    #[test]
181    fn test_fallible_elem_iterator_with_errors() {
182        let parser = create_test_parser_with_errors();
183        let mut iter = parser.into_fallible_elem_iter();
184
185        // First item should be an error
186        let result = iter.next();
187        assert!(result.is_some());
188        assert!(result.unwrap().is_err());
189    }
190
191    #[test]
192    fn test_fallible_elem_iterator_eof() {
193        let parser = create_test_parser_with_valid_data();
194        let mut iter = parser.into_fallible_elem_iter();
195
196        // Should return None on EOF
197        let result = iter.next();
198        assert!(result.is_none());
199    }
200}