1use std::io::Read;
5
6#[macro_use]
7pub mod utils;
8pub mod bgp;
9pub mod bmp;
10pub mod filter;
11pub mod iters;
12pub mod mrt;
13pub mod rpki;
14
15#[cfg(feature = "rislive")]
16pub mod rislive;
17
18pub(crate) use self::utils::*;
19
20use crate::models::MrtRecord;
21pub use mrt::mrt_elem::{BgpUpdateElemIter, ElemError, Elementor, RecordElemIter};
22#[cfg(feature = "oneio")]
23use oneio::{get_cache_reader, get_reader};
24
25pub use crate::error::{ParserError, ParserErrorWithBytes};
26pub use bmp::{parse_bmp_msg, parse_openbmp_header, parse_openbmp_msg};
27pub use filter::*;
28pub use iters::*;
29pub use mrt::*;
30
31#[cfg(feature = "rislive")]
32pub use rislive::parse_ris_live_message;
33
34pub struct BgpkitParser<R> {
35 reader: R,
36 core_dump: bool,
37 filters: Vec<Filter>,
38 options: ParserOptions,
39}
40
41pub(crate) struct ParserOptions {
42 show_warnings: bool,
43}
44impl Default for ParserOptions {
45 fn default() -> Self {
46 ParserOptions {
47 show_warnings: true,
48 }
49 }
50}
51
52#[cfg(feature = "oneio")]
53impl BgpkitParser<Box<dyn Read + Send>> {
54 pub fn new(path: &str) -> Result<Self, ParserErrorWithBytes> {
56 let reader = get_reader(path)?;
57 Ok(BgpkitParser {
58 reader,
59 core_dump: false,
60 filters: vec![],
61 options: ParserOptions::default(),
62 })
63 }
64
65 pub fn new_cached(path: &str, cache_dir: &str) -> Result<Self, ParserErrorWithBytes> {
71 let file_name = path.rsplit('/').next().unwrap().to_string();
72 let new_file_name = format!(
73 "cache-{}",
74 add_suffix_to_filename(file_name.as_str(), crc32(path).as_str())
75 );
76 let reader = get_cache_reader(path, cache_dir, Some(new_file_name), false)?;
77 Ok(BgpkitParser {
78 reader,
79 core_dump: false,
80 filters: vec![],
81 options: ParserOptions::default(),
82 })
83 }
84}
85
86#[cfg(feature = "oneio")]
87fn add_suffix_to_filename(filename: &str, suffix: &str) -> String {
88 let mut parts: Vec<&str> = filename.split('.').collect(); if parts.len() > 1 {
90 let last_part = parts.pop().unwrap(); let new_last_part = format!("{suffix}.{last_part}"); parts.push(&new_last_part); parts.join(".") } else {
95 format!("{filename}.{suffix}")
97 }
98}
99
100impl<R: Read> BgpkitParser<R> {
101 pub fn from_reader(reader: R) -> Self {
103 BgpkitParser {
104 reader,
105 core_dump: false,
106 filters: vec![],
107 options: ParserOptions::default(),
108 }
109 }
110
111 pub fn next_record(&mut self) -> Result<MrtRecord, ParserErrorWithBytes> {
113 parse_mrt_record(&mut self.reader)
114 }
115}
116
117impl<R> BgpkitParser<R> {
118 pub fn enable_core_dump(self) -> Self {
119 BgpkitParser {
120 reader: self.reader,
121 core_dump: true,
122 filters: self.filters,
123 options: self.options,
124 }
125 }
126
127 pub fn disable_warnings(self) -> Self {
128 let mut options = self.options;
129 options.show_warnings = false;
130 BgpkitParser {
131 reader: self.reader,
132 core_dump: self.core_dump,
133 filters: self.filters,
134 options,
135 }
136 }
137
138 pub fn add_filter(
187 self,
188 filter_type: &str,
189 filter_value: &str,
190 ) -> Result<Self, ParserErrorWithBytes> {
191 let mut filters = self.filters;
192 filters.push(Filter::new(filter_type, filter_value)?);
193 Ok(BgpkitParser {
194 reader: self.reader,
195 core_dump: self.core_dump,
196 filters,
197 options: self.options,
198 })
199 }
200
201 pub fn add_filters(mut self, filters: &[Filter]) -> Self {
221 self.filters.extend(filters.iter().cloned());
222 self
223 }
224
225 pub fn with_filters(mut self, filters: &[Filter]) -> Self {
253 self.filters = filters.to_vec();
254 self
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_new_with_reader() {
264 let reader = oneio::get_reader("http://archive.routeviews.org/route-views.ny/bgpdata/2023.02/UPDATES/updates.20230215.0630.bz2").unwrap();
266 assert_eq!(
267 12683,
268 BgpkitParser::from_reader(reader).into_elem_iter().count()
269 );
270
271 let reader = oneio::get_reader("https://spaces.bgpkit.org/parser/update-example").unwrap();
273 assert_eq!(
274 8160,
275 BgpkitParser::from_reader(reader).into_elem_iter().count()
276 );
277 }
278
279 #[test]
280 fn test_new_cached_with_reader() {
281 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
282 let parser = BgpkitParser::new_cached(url, "/tmp/bgpkit-parser-tests")
283 .unwrap()
284 .enable_core_dump()
285 .disable_warnings();
286 let count = parser.into_elem_iter().count();
287 assert_eq!(8160, count);
288 let parser = BgpkitParser::new_cached(url, "/tmp/bgpkit-parser-tests").unwrap();
289 let count = parser.into_elem_iter().count();
290 assert_eq!(8160, count);
291 }
292
293 #[test]
294 fn test_add_suffix_to_filename() {
295 let filename = "example.txt";
297 let suffix = "suffix";
298 let result = add_suffix_to_filename(filename, suffix);
299 assert_eq!(result, "example.suffix.txt");
300
301 let filename = "example.tar.gz";
303 let suffix = "suffix";
304 let result = add_suffix_to_filename(filename, suffix);
305 assert_eq!(result, "example.tar.suffix.gz");
306
307 let filename = "example";
309 let suffix = "suffix";
310 let result = add_suffix_to_filename(filename, suffix);
311 assert_eq!(result, "example.suffix");
312
313 let filename = "";
315 let suffix = "suffix";
316 let result = add_suffix_to_filename(filename, suffix);
317 assert_eq!(result, ".suffix");
318
319 let filename = "example.txt";
321 let suffix = "";
322 let result = add_suffix_to_filename(filename, suffix);
323 assert_eq!(result, "example..txt");
324 }
325
326 #[test]
327 fn test_with_filters() {
328 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
329
330 let filters = vec![
332 Filter::new("peer_ip", "185.1.8.65").unwrap(),
333 Filter::new("type", "w").unwrap(),
334 ];
335
336 let parser = BgpkitParser::new(url).unwrap().with_filters(&filters);
338 let count = parser.into_elem_iter().count();
339
340 assert_eq!(count, 132);
342
343 let filters1 = vec![Filter::new("peer_ip", "185.1.8.65").unwrap()];
345 let filters2 = vec![Filter::new("peer_ip", "185.1.8.50").unwrap()];
346
347 let parser = BgpkitParser::new(url)
348 .unwrap()
349 .with_filters(&filters1)
350 .with_filters(&filters2); let count = parser.into_elem_iter().count();
352
353 assert_eq!(count, 1563);
355 }
356
357 #[test]
358 fn test_add_filters() {
359 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
360
361 let filters = vec![
363 Filter::new("peer_ip", "185.1.8.65").unwrap(),
364 Filter::new("type", "w").unwrap(),
365 ];
366
367 let parser = BgpkitParser::new(url).unwrap().add_filters(&filters);
369 let count = parser.into_elem_iter().count();
370
371 assert_eq!(count, 132);
373
374 let parser = BgpkitParser::new(url)
376 .unwrap()
377 .add_filter("peer_ip", "185.1.8.65")
378 .unwrap()
379 .add_filters(&[Filter::new("type", "w").unwrap()]);
380 let count = parser.into_elem_iter().count();
381 assert_eq!(count, 132);
382 }
383
384 #[test]
385 fn test_with_filters_empty() {
386 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
387
388 let parser = BgpkitParser::new(url).unwrap().with_filters(&[]);
390 let count = parser.into_elem_iter().count();
391
392 assert_eq!(count, 8160);
394 }
395
396 #[test]
397 fn test_add_filters_empty() {
398 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
399
400 let parser = BgpkitParser::new(url)
402 .unwrap()
403 .add_filter("peer_ip", "185.1.8.65")
404 .unwrap()
405 .add_filters(&[]);
406 let count = parser.into_elem_iter().count();
407
408 assert_eq!(count, 3393);
410 }
411
412 #[test]
413 fn test_with_filters_reuse() {
414 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
415
416 let filters = vec![
418 Filter::new("peer_ip", "185.1.8.65").unwrap(),
419 Filter::new("type", "w").unwrap(),
420 ];
421
422 let parser1 = BgpkitParser::new(url).unwrap().with_filters(&filters);
424 let count1 = parser1.into_elem_iter().count();
425
426 let parser2 = BgpkitParser::new(url).unwrap().with_filters(&filters);
427 let count2 = parser2.into_elem_iter().count();
428
429 assert_eq!(count1, 132);
431 assert_eq!(count2, 132);
432 }
433}