-
-
Notifications
You must be signed in to change notification settings - Fork 108
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add response formatter; refactor stats formatter
This adds support for formatting responses in different ways. For now the options are * `plain`: No color, basic formatting * `color`: Color, indented formatting (default) * `emoji`: Fancy mode with emoji icons Fixes #546 Related to #271
- Loading branch information
Showing
22 changed files
with
422 additions
and
234 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use log::Level; | ||
use std::io::Write; | ||
|
||
use crate::{ | ||
formatters::{self, response::TOTAL_RESPONSE_OUTPUT_WIDTH}, | ||
options::ResponseFormat, | ||
verbosity::Verbosity, | ||
}; | ||
|
||
/// Initialize the logging system with the given verbosity level | ||
pub(crate) fn init_logging(verbose: &Verbosity, mode: &ResponseFormat) { | ||
let mut builder = env_logger::Builder::new(); | ||
|
||
builder | ||
.format_timestamp(None) // Disable timestamps | ||
.format_module_path(false) // Disable module path to reduce clutter | ||
.format_target(false) // Disable target | ||
.filter_module("lychee", verbose.log_level_filter()) // Re-add module filtering | ||
.filter_module("lychee_lib", verbose.log_level_filter()); // Re-add module filtering | ||
|
||
// Format the log messages if the output is not plain | ||
if !matches!(mode, ResponseFormat::Plain) { | ||
builder.format(|buf, record| { | ||
let level = record.level(); | ||
|
||
// Spaces added to align the log levels | ||
let level_text = match level { | ||
Level::Error => "ERROR", | ||
Level::Warn => " WARN", | ||
Level::Info => " INFO", | ||
Level::Debug => "DEBUG", | ||
Level::Trace => "TRACE", | ||
}; | ||
|
||
// Calculate the effective padding. Ensure it's non-negative to avoid panic. | ||
let padding = TOTAL_RESPONSE_OUTPUT_WIDTH.saturating_sub(level_text.len() + 2); // +2 for brackets | ||
|
||
// Construct the log prefix with the log level. | ||
let level_label = format!("[{level_text}]"); | ||
let color = match level { | ||
Level::Error => &formatters::color::BOLD_PINK, | ||
Level::Warn => &formatters::color::BOLD_YELLOW, | ||
Level::Info | Level::Debug => &formatters::color::BLUE, | ||
Level::Trace => &formatters::color::DIM, | ||
}; | ||
let colored_level = color.apply_to(level_label); | ||
let prefix = format!("{}{}", " ".repeat(padding), colored_level); | ||
|
||
// Write formatted log message with aligned level and original log message. | ||
writeln!(buf, "{} {}", prefix, record.args()) | ||
}); | ||
} | ||
|
||
builder.init(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,41 @@ | ||
pub(crate) mod color; | ||
pub(crate) mod duration; | ||
pub(crate) mod log; | ||
pub(crate) mod response; | ||
pub(crate) mod stats; | ||
|
||
use lychee_lib::{CacheStatus, ResponseBody, Status}; | ||
use self::{response::ResponseBodyFormatter, stats::StatsFormatter}; | ||
use crate::options::{ResponseFormat, StatsFormat}; | ||
use supports_color::Stream; | ||
|
||
use crate::{ | ||
color::{DIM, GREEN, NORMAL, PINK, YELLOW}, | ||
options::{self, Format}, | ||
}; | ||
|
||
use self::response::ResponseFormatter; | ||
|
||
/// Detects whether a terminal supports color, and gives details about that | ||
/// support. It takes into account the `NO_COLOR` environment variable. | ||
fn supports_color() -> bool { | ||
supports_color::on(Stream::Stdout).is_some() | ||
} | ||
|
||
/// Color the response body for TTYs that support it | ||
pub(crate) fn color_response(body: &ResponseBody) -> String { | ||
if supports_color() { | ||
let out = match body.status { | ||
Status::Ok(_) | Status::Cached(CacheStatus::Ok(_)) => GREEN.apply_to(body), | ||
Status::Excluded | ||
| Status::Unsupported(_) | ||
| Status::Cached(CacheStatus::Excluded | CacheStatus::Unsupported) => { | ||
DIM.apply_to(body) | ||
} | ||
Status::Redirected(_) => NORMAL.apply_to(body), | ||
Status::UnknownStatusCode(_) | Status::Timeout(_) => YELLOW.apply_to(body), | ||
Status::Error(_) | Status::Cached(CacheStatus::Error(_)) => PINK.apply_to(body), | ||
}; | ||
out.to_string() | ||
} else { | ||
body.to_string() | ||
pub(crate) fn get_stats_formatter( | ||
format: &StatsFormat, | ||
response_format: &ResponseFormat, | ||
) -> Box<dyn StatsFormatter> { | ||
match format { | ||
StatsFormat::Compact => Box::new(stats::Compact::new(response_format.clone())), | ||
StatsFormat::Detailed => Box::new(stats::Detailed::new(response_format.clone())), | ||
StatsFormat::Json => Box::new(stats::Json::new()), | ||
StatsFormat::Markdown => Box::new(stats::Markdown::new()), | ||
StatsFormat::Raw => Box::new(stats::Raw::new()), | ||
} | ||
} | ||
|
||
/// Create a response formatter based on the given format option | ||
pub(crate) fn get_formatter(format: &options::Format) -> Box<dyn ResponseFormatter> { | ||
if matches!(format, Format::Raw) || !supports_color() { | ||
return Box::new(response::Raw::new()); | ||
/// | ||
pub(crate) fn get_response_formatter(format: &ResponseFormat) -> Box<dyn ResponseBodyFormatter> { | ||
if !supports_color() { | ||
return Box::new(response::PlainFormatter); | ||
} | ||
match format { | ||
ResponseFormat::Plain => Box::new(response::PlainFormatter), | ||
ResponseFormat::Color => Box::new(response::ColorFormatter), | ||
ResponseFormat::Emoji => Box::new(response::EmojiFormatter), | ||
} | ||
Box::new(response::Color::new()) | ||
} |
Oops, something went wrong.