#![doc = include_str!("../README.md")]
use crate::{args::Args, opt_name::OptName, resolver::RecursiveResolver};
use clap::Parser as _;
use derive_more::Display;
use exn::{Result, ResultExt as _};
use hickory_proto::rr::Name;
use std::str::FromStr as _;
mod args;
mod opt_name;
mod resolver;
#[derive(Debug, Display)]
#[allow(clippy::missing_docs_in_private_items, reason = "self explainatory")]
enum MainError {
#[display("Arguments validation error")]
ArgumentsValidation,
#[display("Resolver creation")]
ResolverCreation,
#[display("Getting first servers")]
GettingFirstServers,
#[display("Converting {_0} to a DNS Name")]
DnsNameConversion(String),
#[display("Creating overview")]
CreatingOverview,
}
impl std::error::Error for MainError {}
#[tokio::main]
#[allow(clippy::exit, reason = "error")]
#[expect(clippy::print_stderr, clippy::print_stdout, reason = "main")]
async fn main() -> Result<(), MainError> {
let mut arguments = Args::parse();
arguments
.validate()
.or_raise(|| MainError::ArgumentsValidation)?;
let recursor = RecursiveResolver::new(&arguments).or_raise(|| MainError::ResolverCreation)?;
let first_servers = recursor
.init()
.await
.or_raise(|| MainError::GettingFirstServers)?;
let name = Name::from_str(&arguments.domain)
.or_raise(|| MainError::DnsNameConversion(arguments.domain.clone()))?;
for (index, first_server) in first_servers.iter().enumerate() {
if index == 0 {
println!(
"Tracing to {}[{}] via {}, maximum of {} retries",
name,
arguments.query_type,
OptName {
zone: None,
..first_server.clone()
},
arguments.retries
);
}
if let Err(error) = recursor
.do_recurse(&name, first_server, 0, Vec::new())
.await
{
eprintln!("{error}");
}
}
if arguments.overview {
recursor
.show_overview()
.or_raise(|| MainError::CreatingOverview)?;
}
Ok(())
}