use anyhow::Result;
#[cfg(feature = "tracing")]
use clap::ArgAction;
use clap::Parser;
#[cfg(feature = "json")]
use netdig::Json;
use netdig::{IpSet, Tabular};
use std::io;
use strum::EnumString;
#[cfg(feature = "tracing")]
use tracing::{debug, trace};
#[derive(Debug, Parser)]
struct Cli {
#[cfg(feature = "tracing")]
#[clap(short, long, action = ArgAction::Count, global = true)]
verbose: u8,
#[clap(short, long, default_value = "text", global = true)]
format: Format,
#[clap(short, long)]
aggregate: bool,
#[clap(short, long)]
truncate: bool,
#[clap(long)]
no_whois: bool,
ips: Vec<String>,
}
#[derive(Debug, Clone, Copy, EnumString)]
#[strum(serialize_all = "snake_case")]
enum Format {
Text,
#[cfg(feature = "json")]
Json,
}
#[allow(clippy::too_many_lines)]
fn main() -> Result<()> {
let cli = Cli::parse();
#[cfg(feature = "tracing")]
{
jacklog::from_level!(cli.verbose + 2)?;
debug!(?cli);
}
let mut ips = IpSet::default();
if cli.ips.is_empty() {
let lines = io::stdin().lines();
for line in lines {
let line = line?;
let line = line.trim();
#[cfg(feature = "tracing")]
trace!(?line);
if line.is_empty() {
continue;
}
ips.insert(line.to_string())?;
}
} else {
for ip in cli.ips {
ips.insert(ip)?;
}
};
let outputs = ips.check_ips(!cli.no_whois)?;
if cli.aggregate {
for out in outputs {
let Some(agg) = out.contained_by else {
continue;
};
println!("{agg}");
}
return Ok(());
}
if cli.truncate {
for out in outputs {
println!("{}", out.canonical);
}
return Ok(());
}
match cli.format {
#[cfg(feature = "json")]
Format::Json => {
println!("{}", outputs.to_json()?);
},
Format::Text => {
for line in outputs.table(atty::is(atty::Stream::Stdout)).lines() {
println!("{}", line.trim());
}
},
}
Ok(())
}