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::messages::{
33 RisLiveClientMessage, RisSubscribe, RisSubscribeSocketOptions, RisSubscribeType,
34};
35#[cfg(feature = "rislive")]
36pub use rislive::{
37 parse_ris_live_message, parse_ris_live_message_json, parse_ris_live_message_raw,
38};
39
40pub struct BgpkitParser<R> {
41 reader: R,
42 core_dump: bool,
43 filters: Vec<Filter>,
44 options: ParserOptions,
45}
46
47pub(crate) struct ParserOptions {
48 show_warnings: bool,
49}
50impl Default for ParserOptions {
51 fn default() -> Self {
52 ParserOptions {
53 show_warnings: true,
54 }
55 }
56}
57
58#[cfg(feature = "oneio")]
59impl BgpkitParser<Box<dyn Read + Send>> {
60 pub fn new(path: &str) -> Result<Self, ParserErrorWithBytes> {
62 let reader = get_reader(path)?;
63 Ok(BgpkitParser {
64 reader,
65 core_dump: false,
66 filters: vec![],
67 options: ParserOptions::default(),
68 })
69 }
70
71 pub fn new_cached(path: &str, cache_dir: &str) -> Result<Self, ParserErrorWithBytes> {
77 let file_name = path.rsplit('/').next().unwrap().to_string();
78 let new_file_name = format!(
79 "cache-{}",
80 add_suffix_to_filename(file_name.as_str(), crc32(path).as_str())
81 );
82 let reader = get_cache_reader(path, cache_dir, Some(new_file_name), false)?;
83 Ok(BgpkitParser {
84 reader,
85 core_dump: false,
86 filters: vec![],
87 options: ParserOptions::default(),
88 })
89 }
90}
91
92#[cfg(feature = "oneio")]
93fn add_suffix_to_filename(filename: &str, suffix: &str) -> String {
94 let mut parts: Vec<&str> = filename.split('.').collect(); if parts.len() > 1 {
96 let last_part = parts.pop().unwrap(); let new_last_part = format!("{suffix}.{last_part}"); parts.push(&new_last_part); parts.join(".") } else {
101 format!("{filename}.{suffix}")
103 }
104}
105
106impl<R: Read> BgpkitParser<R> {
107 pub fn from_reader(reader: R) -> Self {
109 BgpkitParser {
110 reader,
111 core_dump: false,
112 filters: vec![],
113 options: ParserOptions::default(),
114 }
115 }
116
117 pub fn next_record(&mut self) -> Result<MrtRecord, ParserErrorWithBytes> {
119 parse_mrt_record(&mut self.reader)
120 }
121}
122
123impl<R> BgpkitParser<R> {
124 pub fn enable_core_dump(self) -> Self {
125 BgpkitParser {
126 reader: self.reader,
127 core_dump: true,
128 filters: self.filters,
129 options: self.options,
130 }
131 }
132
133 pub fn disable_warnings(self) -> Self {
134 let mut options = self.options;
135 options.show_warnings = false;
136 BgpkitParser {
137 reader: self.reader,
138 core_dump: self.core_dump,
139 filters: self.filters,
140 options,
141 }
142 }
143
144 pub fn add_filter(
193 self,
194 filter_type: &str,
195 filter_value: &str,
196 ) -> Result<Self, ParserErrorWithBytes> {
197 let mut filters = self.filters;
198 filters.push(Filter::new(filter_type, filter_value)?);
199 Ok(BgpkitParser {
200 reader: self.reader,
201 core_dump: self.core_dump,
202 filters,
203 options: self.options,
204 })
205 }
206
207 pub fn add_filters(mut self, filters: &[Filter]) -> Self {
227 self.filters.extend(filters.iter().cloned());
228 self
229 }
230
231 pub fn with_filters(mut self, filters: &[Filter]) -> Self {
259 self.filters = filters.to_vec();
260 self
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 #[test]
269 fn test_new_with_reader() {
270 let reader = oneio::get_reader("http://archive.routeviews.org/route-views.ny/bgpdata/2023.02/UPDATES/updates.20230215.0630.bz2").unwrap();
272 assert_eq!(
273 12683,
274 BgpkitParser::from_reader(reader).into_elem_iter().count()
275 );
276
277 let reader = oneio::get_reader("https://spaces.bgpkit.org/parser/update-example").unwrap();
279 assert_eq!(
280 8160,
281 BgpkitParser::from_reader(reader).into_elem_iter().count()
282 );
283 }
284
285 #[test]
286 fn test_new_cached_with_reader() {
287 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
288 let parser = BgpkitParser::new_cached(url, "/tmp/bgpkit-parser-tests")
289 .unwrap()
290 .enable_core_dump()
291 .disable_warnings();
292 let count = parser.into_elem_iter().count();
293 assert_eq!(8160, count);
294 let parser = BgpkitParser::new_cached(url, "/tmp/bgpkit-parser-tests").unwrap();
295 let count = parser.into_elem_iter().count();
296 assert_eq!(8160, count);
297 }
298
299 #[test]
300 fn test_add_suffix_to_filename() {
301 let filename = "example.txt";
303 let suffix = "suffix";
304 let result = add_suffix_to_filename(filename, suffix);
305 assert_eq!(result, "example.suffix.txt");
306
307 let filename = "example.tar.gz";
309 let suffix = "suffix";
310 let result = add_suffix_to_filename(filename, suffix);
311 assert_eq!(result, "example.tar.suffix.gz");
312
313 let filename = "example";
315 let suffix = "suffix";
316 let result = add_suffix_to_filename(filename, suffix);
317 assert_eq!(result, "example.suffix");
318
319 let filename = "";
321 let suffix = "suffix";
322 let result = add_suffix_to_filename(filename, suffix);
323 assert_eq!(result, ".suffix");
324
325 let filename = "example.txt";
327 let suffix = "";
328 let result = add_suffix_to_filename(filename, suffix);
329 assert_eq!(result, "example..txt");
330 }
331
332 #[test]
333 fn test_with_filters() {
334 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
335
336 let filters = vec![
338 Filter::new("peer_ip", "185.1.8.65").unwrap(),
339 Filter::new("type", "w").unwrap(),
340 ];
341
342 let parser = BgpkitParser::new(url).unwrap().with_filters(&filters);
344 let count = parser.into_elem_iter().count();
345
346 assert_eq!(count, 132);
348
349 let filters1 = vec![Filter::new("peer_ip", "185.1.8.65").unwrap()];
351 let filters2 = vec![Filter::new("peer_ip", "185.1.8.50").unwrap()];
352
353 let parser = BgpkitParser::new(url)
354 .unwrap()
355 .with_filters(&filters1)
356 .with_filters(&filters2); let count = parser.into_elem_iter().count();
358
359 assert_eq!(count, 1563);
361 }
362
363 #[test]
364 fn test_add_filters() {
365 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
366
367 let filters = vec![
369 Filter::new("peer_ip", "185.1.8.65").unwrap(),
370 Filter::new("type", "w").unwrap(),
371 ];
372
373 let parser = BgpkitParser::new(url).unwrap().add_filters(&filters);
375 let count = parser.into_elem_iter().count();
376
377 assert_eq!(count, 132);
379
380 let parser = BgpkitParser::new(url)
382 .unwrap()
383 .add_filter("peer_ip", "185.1.8.65")
384 .unwrap()
385 .add_filters(&[Filter::new("type", "w").unwrap()]);
386 let count = parser.into_elem_iter().count();
387 assert_eq!(count, 132);
388 }
389
390 #[test]
391 fn test_with_filters_empty() {
392 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
393
394 let parser = BgpkitParser::new(url).unwrap().with_filters(&[]);
396 let count = parser.into_elem_iter().count();
397
398 assert_eq!(count, 8160);
400 }
401
402 #[test]
403 fn test_add_filters_empty() {
404 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
405
406 let parser = BgpkitParser::new(url)
408 .unwrap()
409 .add_filter("peer_ip", "185.1.8.65")
410 .unwrap()
411 .add_filters(&[]);
412 let count = parser.into_elem_iter().count();
413
414 assert_eq!(count, 3393);
416 }
417
418 #[test]
419 fn test_with_filters_reuse() {
420 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
421
422 let filters = vec![
424 Filter::new("peer_ip", "185.1.8.65").unwrap(),
425 Filter::new("type", "w").unwrap(),
426 ];
427
428 let parser1 = BgpkitParser::new(url).unwrap().with_filters(&filters);
430 let count1 = parser1.into_elem_iter().count();
431
432 let parser2 = BgpkitParser::new(url).unwrap().with_filters(&filters);
433 let count2 = parser2.into_elem_iter().count();
434
435 assert_eq!(count1, 132);
437 assert_eq!(count2, 132);
438 }
439}