#![allow(clippy::type_complexity)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::expect_used)]
use clap::{Args, Parser, Subcommand};
use monocle::utils::OutputFormat;
use monocle::*;
use tracing::Level;
mod commands;
use commands::as2rel::As2relArgs;
use commands::config::ConfigArgs;
use commands::country::CountryArgs;
use commands::inspect::InspectArgs;
use commands::ip::IpArgs;
use commands::parse::ParseArgs;
use commands::pfx2as::Pfx2asArgs;
use commands::rpki::RpkiCommands;
use commands::search::SearchArgs;
use commands::time::TimeArgs;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
#[clap(propagate_version = true)]
struct Cli {
#[clap(short, long)]
config: Option<String>,
#[clap(long, global = true)]
debug: bool,
#[clap(long, global = true, value_name = "FORMAT")]
format: Option<OutputFormat>,
#[clap(long, global = true)]
json: bool,
#[clap(long, global = true)]
no_update: bool,
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Parse(ParseArgs),
Search(SearchArgs),
Server(ServerArgs),
Inspect(InspectArgs),
Country(CountryArgs),
Time(TimeArgs),
Rpki {
#[clap(subcommand)]
commands: RpkiCommands,
},
Ip(IpArgs),
As2rel(As2relArgs),
Pfx2as(Pfx2asArgs),
Config(ConfigArgs),
}
#[derive(Args, Debug, Clone)]
struct ServerArgs {
#[clap(long, default_value = "127.0.0.1")]
address: String,
#[clap(long, default_value_t = 8080)]
port: u16,
#[clap(long)]
data_dir: Option<String>,
#[clap(long)]
max_concurrent_ops: Option<usize>,
#[clap(long)]
max_message_size: Option<usize>,
#[clap(long)]
connection_timeout_secs: Option<u64>,
#[clap(long)]
ping_interval_secs: Option<u64>,
}
fn main() {
#[cfg(unix)]
{
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
}
dotenvy::dotenv().ok();
let cli = Cli::parse();
let config = match MonocleConfig::new(&cli.config) {
Ok(c) => c,
Err(e) => {
eprintln!("Failed to load configuration: {}", e);
std::process::exit(1);
}
};
if cli.debug {
tracing_subscriber::fmt()
.with_max_level(Level::INFO)
.init();
}
let output_format = if let Some(fmt) = cli.format {
fmt
} else if cli.json {
OutputFormat::JsonPretty
} else {
OutputFormat::Table
};
let streaming_output_format = if let Some(fmt) = cli.format {
fmt
} else if cli.json {
OutputFormat::JsonPretty
} else {
OutputFormat::Psv
};
match cli.command {
Commands::Parse(args) => commands::parse::run(args, streaming_output_format),
Commands::Search(args) => commands::search::run(&config, args, streaming_output_format),
Commands::Server(args) => {
#[cfg(feature = "cli")]
{
let mut server_config = config.clone();
if let Some(data_dir) = args.data_dir {
server_config.data_dir = data_dir;
}
let router = monocle::server::create_router();
let context = monocle::server::WsContext::from_config(server_config);
let mut server_config = monocle::server::ServerConfig::default()
.with_address(args.address)
.with_port(args.port);
if let Some(v) = args.max_concurrent_ops {
server_config.max_concurrent_ops = v;
}
if let Some(v) = args.max_message_size {
server_config.max_message_size = v;
}
if let Some(v) = args.connection_timeout_secs {
server_config.connection_timeout_secs = v;
}
if let Some(v) = args.ping_interval_secs {
server_config.ping_interval_secs = v;
}
let rt = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(e) => {
eprintln!("Failed to create tokio runtime for server: {e}");
std::process::exit(1);
}
};
if let Err(e) = rt.block_on(monocle::server::start_server(
router,
context,
server_config,
)) {
eprintln!("Server failed: {e}");
std::process::exit(1);
}
}
#[cfg(not(feature = "cli"))]
{
let _ = args;
eprintln!("ERROR: server subcommand requires building with --features cli");
std::process::exit(2);
}
}
Commands::Inspect(args) => {
commands::inspect::run(&config, args, output_format, cli.no_update)
}
Commands::Time(args) => commands::time::run(args, output_format),
Commands::Country(args) => commands::country::run(args, output_format),
Commands::Rpki { commands } => {
commands::rpki::run(commands, output_format, &config, cli.no_update)
}
Commands::Ip(args) => commands::ip::run(args, output_format),
Commands::As2rel(args) => {
commands::as2rel::run(&config, args, output_format, cli.no_update)
}
Commands::Pfx2as(args) => {
commands::pfx2as::run(&config, args, output_format, cli.no_update)
}
Commands::Config(args) => commands::config::run(&config, args, output_format),
}
}