add log module and warning/error printing macros

This commit is contained in:
Terts Diepraam
2025-04-22 16:53:08 +02:00
parent 80f4650f7d
commit fa974de9b2
7 changed files with 92 additions and 31 deletions
+5 -10
View File
@@ -13,6 +13,7 @@ use lexopt::Arg;
use crate::env::Env;
use crate::error::Error;
use crate::log::warning;
use crate::parse::TSigInfo;
use crate::Args;
@@ -223,24 +224,18 @@ impl Notify {
}
let Ok(name) = Name::<Vec<u8>>::from_str(server) else {
writeln!(
env.stderr(),
"warning: invalid domain name \"{server}\", skipping."
);
warning!(env, "invalid domain name \"{server}\", skipping.");
continue;
};
let Ok(hosts) = resolver.lookup_host(&name).await else {
writeln!(
env.stderr(),
"warning: could not resolve host \"{name}\", skipping."
);
warning!(env, "could not resolve host \"{name}\", skipping.");
continue;
};
if hosts.is_empty() {
writeln!(
env.stderr(),
warning!(
env,
"skipping bad address: {name}: Name or service not known"
);
continue;
+8 -2
View File
@@ -61,11 +61,17 @@ impl Env for FakeEnv {
}
fn stdout(&self) -> Stream<impl fmt::Write> {
Stream(self.stdout.clone())
Stream {
writer: self.stdout.clone(),
is_terminal: false,
}
}
fn stderr(&self) -> Stream<impl fmt::Write> {
Stream(self.stderr.clone())
Stream {
writer: self.stderr.clone(),
is_terminal: false,
}
}
fn in_cwd<'a>(&self, path: &'a impl AsRef<Path>) -> Cow<'a, Path> {
+9 -2
View File
@@ -59,7 +59,10 @@ pub trait Env {
/// [`std::io::Write`]. Additionally, this `write_fmt` does not return a
/// result. This means that we can use the [`write!`] and [`writeln`] macros
/// without handling errors.
pub struct Stream<T: fmt::Write>(T);
pub struct Stream<T: fmt::Write> {
writer: T,
is_terminal: bool,
}
impl<T: fmt::Write> Stream<T> {
pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) {
@@ -68,7 +71,11 @@ impl<T: fmt::Write> Stream<T> {
// fake stream also does not return an error. If this fails, it means
// we can't write to stdout anymore so a graceful exit will be very
// hard anyway.
self.0.write_fmt(args).unwrap();
self.writer.write_fmt(args).unwrap();
}
pub fn is_terminal(&self) -> bool {
self.is_terminal
}
}
+11 -3
View File
@@ -1,6 +1,6 @@
use std::ffi::OsString;
use std::fmt;
use std::io;
use std::io::{self, IsTerminal};
use std::path::Path;
use domain::net::client::protocol::{AsyncConnect, AsyncDgramRecv, AsyncDgramSend, UdpConnect};
@@ -19,11 +19,19 @@ impl Env for RealEnv {
}
fn stdout(&self) -> Stream<impl fmt::Write> {
Stream(FmtWriter(io::stdout()))
let stdout = io::stdout();
Stream {
is_terminal: stdout.is_terminal(),
writer: FmtWriter(io::stdout()),
}
}
fn stderr(&self) -> Stream<impl fmt::Write> {
Stream(FmtWriter(io::stderr()))
let stderr = io::stderr();
Stream {
is_terminal: stderr.is_terminal(),
writer: FmtWriter(io::stdout()),
}
}
fn in_cwd<'a>(&self, path: &'a impl AsRef<Path>) -> std::borrow::Cow<'a, std::path::Path> {
+4 -14
View File
@@ -4,6 +4,7 @@ use std::{error, io};
use domain::base::wire::ParseError;
use crate::env::Env;
use crate::log::error;
//------------ Error ---------------------------------------------------------
@@ -57,33 +58,22 @@ impl Error {
/// Pretty-print this error.
pub fn pretty_print(&self, env: impl Env) {
use std::io::IsTerminal;
let mut err = env.stderr();
let error = match &self.0.primary {
let msg = match &self.0.primary {
// Clap errors are already styled. We don't want our own pretty
// styling around that and context does not make sense for command
// line arguments either. So we just print the styled string that
// clap produces and return.
PrimaryError::Clap(e) => {
let mut err = env.stderr();
writeln!(err, "{}", e.render().ansi());
return;
}
PrimaryError::Other(error) => error,
};
// NOTE: This is a multicall binary, so argv[0] is necessary for
// program operation. We would fail very early if it didn't exist.
let prog = std::env::args().next().unwrap();
let term = std::io::stderr().is_terminal();
let error_marker = if term {
"\x1B[31mERROR:\x1B[0m"
} else {
"ERROR:"
};
writeln!(err, "[{prog}] {error_marker} {error}");
error!(env, "{msg}");
for context in &self.0.context {
writeln!(err, "... while {context}");
}
+1
View File
@@ -17,6 +17,7 @@ pub mod args;
pub mod commands;
pub mod env;
pub mod error;
pub mod log;
pub mod parse;
pub mod util;
+54
View File
@@ -0,0 +1,54 @@
use std::fmt::Display;
use crate::env::Env;
mod color {
pub const BLUE: u8 = 34;
pub const YELLOW: u8 = 33;
pub const RED: u8 = 31;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LogLevel {
Info,
Warning,
Error,
}
impl LogLevel {
fn color(self) -> u8 {
match self {
Self::Info => color::BLUE,
Self::Warning => color::YELLOW,
Self::Error => color::RED,
}
}
fn text(self) -> &'static str {
match self {
LogLevel::Info => "INFO",
LogLevel::Warning => "WARNING",
LogLevel::Error => "ERROR",
}
}
}
impl Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.text())
}
}
struct Logger(&'static Env);
pub fn log(env: impl Env, level: LogLevel, text: impl Display) {
let mut err = env.stderr();
let prog = std::env::args().next().unwrap();
if err.is_terminal() {
let color = level.color();
writeln!(err, "[{prog}] \x1B[{color}m{level}\x1B[0m: {text}");
} else {
writeln!(err, "[{prog}] {level}: {text}");
}
}