use itertools::Itertools;
use serde_json::json;
use std::io::Write;
use std::net::IpAddr;
use std::path::PathBuf;
use bgpkit_parser::{BgpElem, BgpkitParser, Elementor};
use clap::Parser;
use ipnet::IpNet;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Opts {
#[clap(name = "FILE")]
file_path: PathBuf,
#[clap(short, long)]
cache_dir: Option<PathBuf>,
#[clap(long)]
json: bool,
#[clap(long)]
psv: bool,
#[clap(long)]
pretty: bool,
#[clap(short, long)]
elems_count: bool,
#[clap(short, long)]
records_count: bool,
#[clap(flatten)]
filters: Filters,
}
#[derive(Parser, Debug)]
struct Filters {
#[clap(short = 'o', long)]
origin_asn: Option<u32>,
#[clap(short = 'p', long)]
prefix: Option<IpNet>,
#[clap(short = 's', long)]
include_super: bool,
#[clap(short = 'S', long)]
include_sub: bool,
#[clap(short = '4', long)]
ipv4_only: bool,
#[clap(short = '6', long)]
ipv6_only: bool,
#[clap(short = 'j', long)]
peer_ip: Vec<IpAddr>,
#[clap(short = 'J', long)]
peer_asn: Option<u32>,
#[clap(short = 'm', long)]
elem_type: Option<String>,
#[clap(short = 't', long)]
start_ts: Option<f64>,
#[clap(short = 'T', long)]
end_ts: Option<f64>,
#[clap(short = 'a', long)]
as_path: Option<String>,
#[clap(short = 'C', long)]
community: Option<String>,
}
fn main() {
let opts: Opts = Opts::parse();
env_logger::init();
let file_path = opts.file_path.to_str().unwrap();
let parser_opt = match opts.cache_dir {
None => BgpkitParser::new(file_path),
Some(c) => BgpkitParser::new_cached(file_path, c.to_str().unwrap()),
};
let mut parser = match parser_opt {
Ok(p) => p,
Err(err) => {
eprintln!("{}", err);
std::process::exit(1);
}
};
if let Some(v) = opts.filters.as_path {
parser = parser.add_filter("as_path", v.as_str()).unwrap();
}
if let Some(v) = opts.filters.community {
parser = parser.add_filter("community", v.as_str()).unwrap();
}
if let Some(v) = opts.filters.origin_asn {
parser = parser
.add_filter("origin_asn", v.to_string().as_str())
.unwrap();
}
if let Some(v) = opts.filters.prefix {
let filter_type = match (opts.filters.include_super, opts.filters.include_sub) {
(false, false) => "prefix",
(true, false) => "prefix_super",
(false, true) => "prefix_sub",
(true, true) => "prefix_super_sub",
};
parser = parser
.add_filter(filter_type, v.to_string().as_str())
.unwrap();
}
if !opts.filters.peer_ip.is_empty() {
let v = opts.filters.peer_ip.iter().map(|p| p.to_string()).join(",");
parser = parser.add_filter("peer_ips", v.as_str()).unwrap();
}
if let Some(v) = opts.filters.peer_asn {
parser = parser
.add_filter("peer_asn", v.to_string().as_str())
.unwrap();
}
if let Some(v) = opts.filters.elem_type {
parser = parser.add_filter("type", v.as_str()).unwrap();
}
if let Some(v) = opts.filters.start_ts {
parser = parser
.add_filter("start_ts", v.to_string().as_str())
.unwrap();
}
if let Some(v) = opts.filters.end_ts {
parser = parser.add_filter("end_ts", v.to_string().as_str()).unwrap();
}
match (opts.filters.ipv4_only, opts.filters.ipv6_only) {
(true, true) => {
eprintln!("Error: --ipv4-only and --ipv6-only cannot be used together");
std::process::exit(1);
}
(false, false) => {
}
(true, false) => {
parser = parser.add_filter("ip_version", "ipv4").unwrap();
}
(false, true) => {
parser = parser.add_filter("ip_version", "ipv6").unwrap();
}
}
match (opts.elems_count, opts.records_count) {
(true, true) => {
let mut elementor = Elementor::new();
let (mut records_count, mut elems_count) = (0, 0);
for record in parser.into_record_iter() {
records_count += 1;
elems_count += elementor.record_to_elems(record).len();
}
println!("total records: {}", records_count);
println!("total elems: {}", elems_count);
}
(false, true) => {
println!("total records: {}", parser.into_record_iter().count());
}
(true, false) => {
println!("total records: {}", parser.into_elem_iter().count());
}
(false, false) => {
let mut stdout = std::io::stdout();
for (index, elem) in parser.into_elem_iter().enumerate() {
let output_str = if opts.json {
let val = json!(elem);
if opts.pretty {
serde_json::to_string_pretty(&val).unwrap()
} else {
val.to_string()
}
} else if opts.psv {
if index == 0 {
format!("{}\n{}", BgpElem::get_psv_header(), elem.to_psv())
} else {
elem.to_psv()
}
} else {
elem.to_string()
};
if let Err(e) = writeln!(stdout, "{}", &output_str) {
if e.kind() != std::io::ErrorKind::BrokenPipe {
eprintln!("{}", e);
}
std::process::exit(1);
}
}
}
}
}