bgpkit_commons/lib.rs
1//! # Overview
2//!
3//! BGPKIT-Commons is a library for common BGP-related data and functions with a lazy-loading architecture.
4//! Each module can be independently enabled via feature flags, allowing for minimal builds.
5//!
6//! ## Available Modules
7//!
8//! ### [`asinfo`] - Autonomous System Information (requires `asinfo` feature)
9//! **Load Methods**: `load_asinfo(as2org, population, hegemony, peeringdb)`, `load_asinfo_cached()`, or `load_asinfo_with(builder)`
10//! **Access Methods**: `asinfo_get(asn)`, `asinfo_all()`, `asinfo_are_siblings(asn1, asn2)`
11//! **Data Sources**: RIPE NCC, CAIDA as2org, APNIC population, IIJ IHR hegemony, PeeringDB
12//! **Functionality**: AS name resolution, country mapping, organization data, population statistics, hegemony scores, sibling detection
13//!
14//! **New in v0.10.0**: The `as2org-rs` and `peeringdb-rs` crates have been consolidated into this module.
15//! Use [`AsInfoBuilder`](asinfo::AsInfoBuilder) for ergonomic configuration of data sources.
16//!
17//! ### [`as2rel`] - AS Relationship Data (requires `as2rel` feature)
18//! **Load Method**: `load_as2rel()`
19//! **Access Methods**: `as2rel_lookup(asn1, asn2)`
20//! **Data Sources**: BGPKIT AS relationship inference
21//! **Functionality**: Provider-customer, peer-to-peer, and sibling relationships between ASes
22//!
23//! ### [`bogons`] - Bogon Detection (requires `bogons` feature)
24//! **Load Method**: `load_bogons()`
25//! **Access Methods**: `bogons_match(input)`, `bogons_match_prefix(prefix)`, `bogons_match_asn(asn)`, `get_bogon_prefixes()`, `get_bogon_asns()`
26//! **Data Sources**: IANA special registries (IPv4, IPv6, ASN)
27//! **Functionality**: Detect invalid/reserved IP prefixes and ASNs that shouldn't appear in routing
28//!
29//! ### [`countries`] - Country Information (requires `countries` feature)
30//! **Load Method**: `load_countries()`
31//! **Access Methods**: `country_by_code(code)`, `country_by_code3(code)`, `country_by_name(name)`, `country_all()`
32//! **Data Sources**: GeoNames geographical database
33//! **Functionality**: ISO country code to name mapping and geographical information
34//!
35//! ### [`mrt_collectors`] - MRT Collector Metadata (requires `mrt_collectors` feature)
36//! **Load Methods**: `load_mrt_collectors()`, `load_mrt_collector_peers()`
37//! **Access Methods**: `mrt_collectors_all()`, `mrt_collectors_by_name(name)`, `mrt_collectors_by_country(country)`, `mrt_collector_peers_all()`, `mrt_collector_peers_full_feed()`
38//! **Data Sources**: RouteViews and RIPE RIS official APIs
39//! **Functionality**: BGP collector information, peer details, full-feed vs partial-feed classification
40//!
41//! ### [`rpki`] - RPKI Validation (requires `rpki` feature)
42//! **Load Methods**: `load_rpki(optional_date)`, `load_rpki_historical(date, source)`, `load_rpki_from_files(urls, source, date)`
43//! **Access Methods**: `rpki_validate(asn, prefix)`, `rpki_validate_check_expiry(asn, prefix, timestamp)`, `rpki_lookup_by_prefix(prefix)`
44//! **Data Sources**: Cloudflare real-time, RIPE NCC historical, RPKIviews historical
45//! **Functionality**: Route Origin Authorization (ROA) and ASPA validation, supports multiple data sources
46//!
47//! **New in v0.10.0**: Added RPKIviews as a historical RPKI data source with multiple collectors.
48//! New public types [`Roa`](rpki::Roa) and [`Aspa`](rpki::Aspa) provide stable API for RPKI objects.
49//!
50//! ## Quick Start
51//!
52//! Add `bgpkit-commons` to your `Cargo.toml`:
53//! ```toml
54//! [dependencies]
55//! bgpkit-commons = "0.10"
56//! ```
57//!
58//! ### Basic Usage Pattern
59//!
60//! All modules follow the same lazy-loading pattern:
61//! 1. Create a mutable `BgpkitCommons` instance
62//! 2. Load the data you need by calling `load_xxx()` methods
63//! 3. Access the data using the corresponding `xxx_yyy()` methods
64//!
65//! ```rust
66//! # #[cfg(feature = "bogons")]
67//! # fn main() {
68//! use bgpkit_commons::BgpkitCommons;
69//!
70//! let mut commons = BgpkitCommons::new();
71//!
72//! // Load bogon data
73//! commons.load_bogons().unwrap();
74//!
75//! // Use the data
76//! if let Ok(is_bogon) = commons.bogons_match("23456") {
77//! println!("ASN 23456 is a bogon: {}", is_bogon);
78//! }
79//! # }
80//! # #[cfg(not(feature = "bogons"))]
81//! # fn main() {}
82//! ```
83//!
84//! ### Working with Multiple Modules
85//!
86//! ```rust
87//! # #[cfg(all(feature = "asinfo", feature = "countries"))]
88//! # fn main() {
89//! use bgpkit_commons::BgpkitCommons;
90//!
91//! let mut commons = BgpkitCommons::new();
92//!
93//! // Load multiple data sources
94//! commons.load_asinfo(false, false, false, false).unwrap();
95//! commons.load_countries().unwrap();
96//!
97//! // Use the data together
98//! if let Ok(Some(asinfo)) = commons.asinfo_get(13335) {
99//! println!("AS13335: {} ({})", asinfo.name, asinfo.country);
100//! }
101//! # }
102//! # #[cfg(not(all(feature = "asinfo", feature = "countries")))]
103//! # fn main() {}
104//! ```
105//!
106//! ### Using the AsInfoBuilder (New in v0.10.0)
107//!
108//! The builder pattern provides a clearer API for loading AS information:
109//!
110//! ```rust
111//! # #[cfg(feature = "asinfo")]
112//! # fn main() {
113//! use bgpkit_commons::BgpkitCommons;
114//!
115//! let mut commons = BgpkitCommons::new();
116//!
117//! // Clear, self-documenting configuration
118//! let builder = commons.asinfo_builder()
119//! .with_as2org()
120//! .with_peeringdb();
121//! commons.load_asinfo_with(builder).unwrap();
122//!
123//! // Check if two ASes are siblings (requires as2org data)
124//! if let Ok(are_siblings) = commons.asinfo_are_siblings(13335, 132892) {
125//! println!("AS13335 and AS132892 are siblings: {}", are_siblings);
126//! }
127//! # }
128//! # #[cfg(not(feature = "asinfo"))]
129//! # fn main() {}
130//! ```
131//!
132//! ### Loading Historical RPKI Data (New in v0.10.0)
133//!
134//! Load RPKI data from multiple historical sources:
135//!
136//! ```rust,no_run
137//! # #[cfg(feature = "rpki")]
138//! # fn main() {
139//! use bgpkit_commons::BgpkitCommons;
140//! use bgpkit_commons::rpki::{HistoricalRpkiSource, RpkiViewsCollector};
141//! use chrono::NaiveDate;
142//!
143//! let mut commons = BgpkitCommons::new();
144//! let date = NaiveDate::from_ymd_opt(2024, 1, 4).unwrap();
145//!
146//! // Load from RIPE NCC historical archives
147//! commons.load_rpki_historical(date, HistoricalRpkiSource::Ripe).unwrap();
148//!
149//! // Or load from RPKIviews collectors
150//! let source = HistoricalRpkiSource::RpkiViews(RpkiViewsCollector::KerfuffleNet);
151//! commons.load_rpki_historical(date, source).unwrap();
152//!
153//! // List available files for a date
154//! let files = commons.list_rpki_files(date, HistoricalRpkiSource::Ripe).unwrap();
155//! # }
156//! # #[cfg(not(feature = "rpki"))]
157//! # fn main() {}
158//! ```
159//!
160//! ## Feature Flags
161//!
162//! ### Module Features
163//! - `asinfo` - AS information with organization and population data (includes integrated as2org and peeringdb)
164//! - `as2rel` - AS relationship data
165//! - `bogons` - Bogon prefix and ASN detection
166//! - `countries` - Country information lookup
167//! - `mrt_collectors` - MRT collector metadata
168//! - `rpki` - RPKI validation functionality (ROA and ASPA)
169//!
170//! ### Convenience Features
171//! - `all` (default) - Enables all modules for backwards compatibility
172//!
173//! ### Minimal Build Example
174//! ```toml
175//! [dependencies]
176//! bgpkit-commons = { version = "0.10", default-features = false, features = ["bogons", "countries"] }
177//! ```
178//!
179//! ## Direct Module Access
180//!
181//! All modules support both central access via `BgpkitCommons` and direct module access:
182//!
183//! ```rust
184//! # #[cfg(feature = "bogons")]
185//! # fn main() {
186//! // Via BgpkitCommons (recommended for most use cases)
187//! use bgpkit_commons::BgpkitCommons;
188//! let mut commons = BgpkitCommons::new();
189//! commons.load_bogons().unwrap();
190//!
191//! // Direct module access (useful for standalone usage)
192//! use bgpkit_commons::bogons::Bogons;
193//! let bogons = Bogons::new().unwrap();
194//! # }
195//! # #[cfg(not(feature = "bogons"))]
196//! # fn main() {}
197//! ```
198//!
199//! ## Error Handling
200//!
201//! All access methods return `Result<T>` and will return an error if the corresponding module
202//! hasn't been loaded yet or if there are data validation issues. Error messages include guidance
203//! on which `load_xxx()` method to call. Always call the appropriate `load_xxx()` method before accessing data.
204//!
205//! ## Data Persistence and Reloading
206//!
207//! All loaded data is kept in memory for fast access. Use the `reload()` method to refresh
208//! all currently loaded data sources:
209//!
210//! ```rust
211//! # #[cfg(feature = "bogons")]
212//! # fn main() {
213//! # use bgpkit_commons::BgpkitCommons;
214//! let mut commons = BgpkitCommons::new();
215//! commons.load_bogons().unwrap();
216//!
217//! // Later, reload all loaded data
218//! commons.reload().unwrap();
219//! # }
220//! # #[cfg(not(feature = "bogons"))]
221//! # fn main() {}
222//! ```
223//!
224//! ## What's New in v0.10.0
225//!
226//! - **RPKIviews Historical Data**: Load historical RPKI data from RPKIviews collectors (SoborostNet, MassarsNet, AttnJp, KerfuffleNet) in addition to RIPE NCC archives
227//! - **Crate Consolidation**: `as2org-rs` and `peeringdb-rs` functionality integrated directly into the `asinfo` module
228//! - **AsInfoBuilder**: New builder pattern for ergonomic configuration of AS information data sources
229//! - **Public RPKI Types**: New stable [`Roa`](rpki::Roa) and [`Aspa`](rpki::Aspa) structs for RPKI objects
230//! - **Streaming Optimization**: RPKIviews archives are streamed efficiently without downloading entire files
231//! - **RIPE Historical JSON**: RIPE historical data now uses JSON format for richer data including expiry timestamps
232
233#![doc(
234 html_logo_url = "https://raw.githubusercontent.com/bgpkit/assets/main/logos/icon-transparent.png",
235 html_favicon_url = "https://raw.githubusercontent.com/bgpkit/assets/main/logos/favicon.ico"
236)]
237
238#[cfg(feature = "as2rel")]
239pub mod as2rel;
240#[cfg(feature = "asinfo")]
241pub mod asinfo;
242#[cfg(feature = "bogons")]
243pub mod bogons;
244#[cfg(feature = "countries")]
245pub mod countries;
246#[cfg(feature = "mrt_collectors")]
247pub mod mrt_collectors;
248#[cfg(feature = "rpki")]
249pub mod rpki;
250
251pub mod errors;
252
253// Re-export error types for convenience
254pub use errors::{BgpkitCommonsError, Result};
255
256/// Trait for modules that support lazy loading and reloading of data
257pub trait LazyLoadable {
258 /// Reload the module's data from its external sources
259 fn reload(&mut self) -> Result<()>;
260
261 /// Check if the module's data is currently loaded
262 fn is_loaded(&self) -> bool;
263
264 /// Get a description of the module's current loading status
265 fn loading_status(&self) -> &'static str;
266}
267
268#[derive(Default)]
269pub struct BgpkitCommons {
270 #[cfg(feature = "countries")]
271 countries: Option<crate::countries::Countries>,
272 #[cfg(feature = "rpki")]
273 rpki_trie: Option<crate::rpki::RpkiTrie>,
274 #[cfg(feature = "mrt_collectors")]
275 mrt_collectors: Option<Vec<crate::mrt_collectors::MrtCollector>>,
276 #[cfg(feature = "mrt_collectors")]
277 mrt_collector_peers: Option<Vec<crate::mrt_collectors::MrtCollectorPeer>>,
278 #[cfg(feature = "bogons")]
279 bogons: Option<crate::bogons::Bogons>,
280 #[cfg(feature = "asinfo")]
281 asinfo: Option<crate::asinfo::AsInfoUtils>,
282 #[cfg(feature = "as2rel")]
283 as2rel: Option<crate::as2rel::As2relBgpkit>,
284}
285
286impl BgpkitCommons {
287 pub fn new() -> Self {
288 Self::default()
289 }
290
291 /// Reload all data sources that are already loaded
292 pub fn reload(&mut self) -> Result<()> {
293 #[cfg(feature = "countries")]
294 if self.countries.is_some() {
295 self.load_countries()?;
296 }
297 #[cfg(feature = "rpki")]
298 if let Some(rpki) = self.rpki_trie.as_mut() {
299 rpki.reload()?;
300 }
301 #[cfg(feature = "mrt_collectors")]
302 if self.mrt_collectors.is_some() {
303 self.load_mrt_collectors()?;
304 }
305 #[cfg(feature = "mrt_collectors")]
306 if self.mrt_collector_peers.is_some() {
307 self.load_mrt_collector_peers()?;
308 }
309 #[cfg(feature = "bogons")]
310 if self.bogons.is_some() {
311 self.load_bogons()?;
312 }
313 #[cfg(feature = "asinfo")]
314 if let Some(asinfo) = self.asinfo.as_mut() {
315 asinfo.reload()?;
316 }
317 #[cfg(feature = "as2rel")]
318 if self.as2rel.is_some() {
319 self.load_as2rel()?;
320 }
321
322 Ok(())
323 }
324
325 /// Get loading status for all available modules
326 pub fn loading_status(&self) -> Vec<(&'static str, &'static str)> {
327 #[allow(unused_mut)] // mut needed when any features are enabled
328 let mut status = Vec::new();
329
330 #[cfg(feature = "countries")]
331 if let Some(ref countries) = self.countries {
332 status.push(("countries", countries.loading_status()));
333 } else {
334 status.push(("countries", "Countries data not loaded"));
335 }
336
337 #[cfg(feature = "bogons")]
338 if let Some(ref bogons) = self.bogons {
339 status.push(("bogons", bogons.loading_status()));
340 } else {
341 status.push(("bogons", "Bogons data not loaded"));
342 }
343
344 #[cfg(feature = "rpki")]
345 if let Some(ref rpki) = self.rpki_trie {
346 status.push(("rpki", rpki.loading_status()));
347 } else {
348 status.push(("rpki", "RPKI data not loaded"));
349 }
350
351 #[cfg(feature = "asinfo")]
352 if let Some(ref asinfo) = self.asinfo {
353 status.push(("asinfo", asinfo.loading_status()));
354 } else {
355 status.push(("asinfo", "ASInfo data not loaded"));
356 }
357
358 #[cfg(feature = "as2rel")]
359 if let Some(ref as2rel) = self.as2rel {
360 status.push(("as2rel", as2rel.loading_status()));
361 } else {
362 status.push(("as2rel", "AS2Rel data not loaded"));
363 }
364
365 #[cfg(feature = "mrt_collectors")]
366 {
367 if self.mrt_collectors.is_some() {
368 status.push(("mrt_collectors", "MRT collectors data loaded"));
369 } else {
370 status.push(("mrt_collectors", "MRT collectors data not loaded"));
371 }
372
373 if self.mrt_collector_peers.is_some() {
374 status.push(("mrt_collector_peers", "MRT collector peers data loaded"));
375 } else {
376 status.push(("mrt_collector_peers", "MRT collector peers data not loaded"));
377 }
378 }
379
380 status
381 }
382
383 /// Load countries data
384 #[cfg(feature = "countries")]
385 pub fn load_countries(&mut self) -> Result<()> {
386 self.countries = Some(crate::countries::Countries::new()?);
387 Ok(())
388 }
389
390 /// Load RPKI data from Cloudflare (real-time) or historical archives
391 ///
392 /// - If `date_opt` is `None`, loads real-time data from Cloudflare
393 /// - If `date_opt` is `Some(date)`, loads historical data from RIPE NCC by default
394 ///
395 /// For more control over the data source, use `load_rpki_historical()` instead.
396 #[cfg(feature = "rpki")]
397 pub fn load_rpki(&mut self, date_opt: Option<chrono::NaiveDate>) -> Result<()> {
398 if let Some(date) = date_opt {
399 self.rpki_trie = Some(rpki::RpkiTrie::from_ripe_historical(date)?);
400 } else {
401 self.rpki_trie = Some(rpki::RpkiTrie::from_cloudflare()?);
402 }
403 Ok(())
404 }
405
406 /// Load RPKI data from a specific historical data source
407 ///
408 /// This allows you to choose between RIPE NCC and RPKIviews for historical data.
409 ///
410 /// # Example
411 ///
412 /// ```rust,no_run
413 /// use bgpkit_commons::BgpkitCommons;
414 /// use bgpkit_commons::rpki::{HistoricalRpkiSource, RpkiViewsCollector};
415 /// use chrono::NaiveDate;
416 ///
417 /// let mut commons = BgpkitCommons::new();
418 /// let date = NaiveDate::from_ymd_opt(2024, 1, 4).unwrap();
419 ///
420 /// // Load from RIPE NCC
421 /// commons.load_rpki_historical(date, HistoricalRpkiSource::Ripe).unwrap();
422 ///
423 /// // Or load from RPKIviews
424 /// let source = HistoricalRpkiSource::RpkiViews(RpkiViewsCollector::KerfuffleNet);
425 /// commons.load_rpki_historical(date, source).unwrap();
426 /// ```
427 #[cfg(feature = "rpki")]
428 pub fn load_rpki_historical(
429 &mut self,
430 date: chrono::NaiveDate,
431 source: rpki::HistoricalRpkiSource,
432 ) -> Result<()> {
433 match source {
434 rpki::HistoricalRpkiSource::Ripe => {
435 self.rpki_trie = Some(rpki::RpkiTrie::from_ripe_historical(date)?);
436 }
437 rpki::HistoricalRpkiSource::RpkiViews(collector) => {
438 self.rpki_trie = Some(rpki::RpkiTrie::from_rpkiviews(collector, date)?);
439 }
440 }
441 Ok(())
442 }
443
444 /// Load RPKI data from specific file URLs
445 ///
446 /// This allows loading from specific archive files, which is useful when you want
447 /// to process multiple files or use specific timestamps.
448 ///
449 /// # Arguments
450 ///
451 /// * `urls` - A slice of URLs pointing to RPKI data files
452 /// * `source` - The type of data source (RIPE or RPKIviews) - determines how files are parsed
453 /// * `date` - Optional date to associate with the loaded data
454 ///
455 /// # Example
456 ///
457 /// ```rust,no_run
458 /// use bgpkit_commons::BgpkitCommons;
459 /// use bgpkit_commons::rpki::HistoricalRpkiSource;
460 ///
461 /// let mut commons = BgpkitCommons::new();
462 /// let urls = vec![
463 /// "https://example.com/rpki-20240104T144128Z.tgz".to_string(),
464 /// ];
465 /// commons.load_rpki_from_files(&urls, HistoricalRpkiSource::RpkiViews(
466 /// bgpkit_commons::rpki::RpkiViewsCollector::KerfuffleNet
467 /// ), None).unwrap();
468 /// ```
469 #[cfg(feature = "rpki")]
470 pub fn load_rpki_from_files(
471 &mut self,
472 urls: &[String],
473 source: rpki::HistoricalRpkiSource,
474 date: Option<chrono::NaiveDate>,
475 ) -> Result<()> {
476 match source {
477 rpki::HistoricalRpkiSource::Ripe => {
478 self.rpki_trie = Some(rpki::RpkiTrie::from_ripe_files(urls, date)?);
479 }
480 rpki::HistoricalRpkiSource::RpkiViews(_) => {
481 self.rpki_trie = Some(rpki::RpkiTrie::from_rpkiviews_files(urls, date)?);
482 }
483 }
484 Ok(())
485 }
486
487 /// List available RPKI files for a given date from a specific source
488 ///
489 /// # Example
490 ///
491 /// ```rust,no_run
492 /// use bgpkit_commons::BgpkitCommons;
493 /// use bgpkit_commons::rpki::{HistoricalRpkiSource, RpkiViewsCollector};
494 /// use chrono::NaiveDate;
495 ///
496 /// let commons = BgpkitCommons::new();
497 /// let date = NaiveDate::from_ymd_opt(2024, 1, 4).unwrap();
498 ///
499 /// // List files from RIPE NCC
500 /// let ripe_files = commons.list_rpki_files(date, HistoricalRpkiSource::Ripe).unwrap();
501 ///
502 /// // List files from RPKIviews
503 /// let source = HistoricalRpkiSource::RpkiViews(RpkiViewsCollector::KerfuffleNet);
504 /// let rpkiviews_files = commons.list_rpki_files(date, source).unwrap();
505 /// ```
506 #[cfg(feature = "rpki")]
507 pub fn list_rpki_files(
508 &self,
509 date: chrono::NaiveDate,
510 source: rpki::HistoricalRpkiSource,
511 ) -> Result<Vec<rpki::RpkiFile>> {
512 match source {
513 rpki::HistoricalRpkiSource::Ripe => rpki::list_ripe_files(date),
514 rpki::HistoricalRpkiSource::RpkiViews(collector) => {
515 rpki::list_rpkiviews_files(collector, date)
516 }
517 }
518 }
519
520 /// Load MRT mrt_collectors data
521 #[cfg(feature = "mrt_collectors")]
522 pub fn load_mrt_collectors(&mut self) -> Result<()> {
523 self.mrt_collectors = Some(crate::mrt_collectors::get_all_collectors()?);
524 Ok(())
525 }
526
527 /// Load MRT mrt_collectors data
528 #[cfg(feature = "mrt_collectors")]
529 pub fn load_mrt_collector_peers(&mut self) -> Result<()> {
530 self.mrt_collector_peers = Some(crate::mrt_collectors::get_mrt_collector_peers()?);
531 Ok(())
532 }
533
534 /// Load bogons data
535 #[cfg(feature = "bogons")]
536 pub fn load_bogons(&mut self) -> Result<()> {
537 self.bogons = Some(crate::bogons::Bogons::new()?);
538 Ok(())
539 }
540
541 /// Load AS name and country data
542 #[cfg(feature = "asinfo")]
543 pub fn load_asinfo(
544 &mut self,
545 load_as2org: bool,
546 load_population: bool,
547 load_hegemony: bool,
548 load_peeringdb: bool,
549 ) -> Result<()> {
550 self.asinfo = Some(crate::asinfo::AsInfoUtils::new(
551 load_as2org,
552 load_population,
553 load_hegemony,
554 load_peeringdb,
555 )?);
556 Ok(())
557 }
558
559 #[cfg(feature = "asinfo")]
560 pub fn load_asinfo_cached(&mut self) -> Result<()> {
561 self.asinfo = Some(crate::asinfo::AsInfoUtils::new_from_cached()?);
562 Ok(())
563 }
564
565 /// Returns a builder for loading AS information with specific data sources.
566 ///
567 /// This provides a more ergonomic way to configure which data sources to load
568 /// compared to the `load_asinfo()` method with boolean parameters.
569 ///
570 /// # Example
571 ///
572 /// ```rust,no_run
573 /// use bgpkit_commons::BgpkitCommons;
574 ///
575 /// let mut commons = BgpkitCommons::new();
576 /// let builder = commons.asinfo_builder()
577 /// .with_as2org()
578 /// .with_peeringdb();
579 /// commons.load_asinfo_with(builder).unwrap();
580 /// ```
581 #[cfg(feature = "asinfo")]
582 pub fn asinfo_builder(&self) -> crate::asinfo::AsInfoBuilder {
583 crate::asinfo::AsInfoBuilder::new()
584 }
585
586 /// Load AS information using a pre-configured builder.
587 ///
588 /// # Example
589 ///
590 /// ```rust,no_run
591 /// use bgpkit_commons::BgpkitCommons;
592 ///
593 /// let mut commons = BgpkitCommons::new();
594 /// let builder = commons.asinfo_builder()
595 /// .with_as2org()
596 /// .with_hegemony();
597 /// commons.load_asinfo_with(builder).unwrap();
598 /// ```
599 #[cfg(feature = "asinfo")]
600 pub fn load_asinfo_with(&mut self, builder: crate::asinfo::AsInfoBuilder) -> Result<()> {
601 self.asinfo = Some(builder.build()?);
602 Ok(())
603 }
604
605 /// Load AS-level relationship data
606 #[cfg(feature = "as2rel")]
607 pub fn load_as2rel(&mut self) -> Result<()> {
608 self.as2rel = Some(crate::as2rel::As2relBgpkit::new()?);
609 Ok(())
610 }
611}