Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lychee-bin/src/commands/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ use lychee_lib::{ResponseBody, Status};

use crate::formatters::get_progress_formatter;
use crate::formatters::response::ResponseFormatter;
use crate::formatters::stats::ResponseStats;
use crate::formatters::suggestion::Suggestion;
use crate::parse::parse_duration_secs;
use crate::progress::Progress;
use crate::{ExitCode, cache::Cache, stats::ResponseStats};
use crate::{ExitCode, cache::Cache};

use super::CommandParams;

Expand Down
31 changes: 5 additions & 26 deletions lychee-bin/src/formatters/host_stats/compact.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
use anyhow::Result;
use std::{
collections::HashMap,
fmt::{self, Display},
};
use std::fmt::{self, Display};

use crate::formatters::color::{DIM, NORMAL, color};
use lychee_lib::ratelimit::HostStats;
use lychee_lib::ratelimit::HostStatsMap;

use super::HostStatsFormatter;

struct CompactHostStats {
host_stats: HashMap<String, HostStats>,
pub(crate) struct CompactHostStats {
pub(crate) host_stats: HostStatsMap,
}

impl Display for CompactHostStats {
Expand All @@ -26,7 +20,7 @@ impl Display for CompactHostStats {
color!(f, DIM, "{}", separator)?;
writeln!(f)?;

let sorted_hosts = super::sort_host_stats(&self.host_stats);
let sorted_hosts = self.host_stats.sorted();

// Calculate optimal hostname width based on longest hostname
let max_hostname_len = sorted_hosts
Expand Down Expand Up @@ -60,18 +54,3 @@ impl Display for CompactHostStats {
Ok(())
}
}

pub(crate) struct Compact;

impl Compact {
pub(crate) const fn new() -> Self {
Self
}
}

impl HostStatsFormatter for Compact {
fn format(&self, host_stats: HashMap<String, HostStats>) -> Result<String> {
let compact = CompactHostStats { host_stats };
Ok(compact.to_string())
}
}
33 changes: 5 additions & 28 deletions lychee-bin/src/formatters/host_stats/detailed.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
use anyhow::Result;
use std::{
collections::HashMap,
fmt::{self, Display},
};
use std::fmt::{self, Display};

use lychee_lib::ratelimit::HostStats;
use lychee_lib::ratelimit::HostStatsMap;

use super::HostStatsFormatter;

struct DetailedHostStats {
host_stats: HashMap<String, HostStats>,
pub(crate) struct DetailedHostStats {
pub(crate) host_stats: HostStatsMap,
}

impl Display for DetailedHostStats {
Expand All @@ -21,9 +15,7 @@ impl Display for DetailedHostStats {
writeln!(f, "\n📊 Per-host Statistics")?;
writeln!(f, "---------------------")?;

let sorted_hosts = super::sort_host_stats(&self.host_stats);

for (hostname, stats) in sorted_hosts {
for (hostname, stats) in self.host_stats.sorted() {
writeln!(f, "\nHost: {hostname}")?;
writeln!(f, " Total requests: {}", stats.total_requests)?;
writeln!(
Expand Down Expand Up @@ -69,18 +61,3 @@ impl Display for DetailedHostStats {
Ok(())
}
}

pub(crate) struct Detailed;

impl Detailed {
pub(crate) const fn new() -> Self {
Self
}
}

impl HostStatsFormatter for Detailed {
fn format(&self, host_stats: HashMap<String, HostStats>) -> Result<String> {
let detailed = DetailedHostStats { host_stats };
Ok(detailed.to_string())
}
}
51 changes: 0 additions & 51 deletions lychee-bin/src/formatters/host_stats/json.rs

This file was deleted.

62 changes: 22 additions & 40 deletions lychee-bin/src/formatters/host_stats/markdown.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
use std::{
collections::HashMap,
fmt::{self, Display},
};
use std::fmt::{self, Display};

use super::HostStatsFormatter;
use anyhow::Result;
use lychee_lib::ratelimit::HostStats;
use lychee_lib::ratelimit::HostStatsMap;
use tabled::{
Table, Tabled,
settings::{Alignment, Modify, Style, object::Segment},
};

pub(crate) struct MarkdownHostStats {
pub(crate) host_stats: HostStatsMap,
}

impl Display for MarkdownHostStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.host_stats.is_empty() {
return Ok(());
}

writeln!(f, "\n## Per-host Statistics")?;
writeln!(f)?;
writeln!(f, "{}", host_stats_table(&self.host_stats))?;

Ok(())
}
}

#[derive(Tabled)]
struct HostStatsTableEntry {
#[tabled(rename = "Host")]
Expand All @@ -25,8 +38,8 @@ struct HostStatsTableEntry {
cache_hit_rate: String,
}

fn host_stats_table(host_stats: &HashMap<String, HostStats>) -> String {
let sorted_hosts = super::sort_host_stats(host_stats);
fn host_stats_table(host_stats: &HostStatsMap) -> String {
let sorted_hosts = host_stats.sorted();

let entries: Vec<HostStatsTableEntry> = sorted_hosts
.into_iter()
Expand Down Expand Up @@ -55,34 +68,3 @@ fn host_stats_table(host_stats: &HashMap<String, HostStats>) -> String {
.with(style)
.to_string()
}

struct MarkdownHostStats(HashMap<String, HostStats>);

impl Display for MarkdownHostStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.is_empty() {
return Ok(());
}

writeln!(f, "\n## Per-host Statistics")?;
writeln!(f)?;
writeln!(f, "{}", host_stats_table(&self.0))?;

Ok(())
}
}

pub(crate) struct Markdown;

impl Markdown {
pub(crate) const fn new() -> Self {
Self {}
}
}

impl HostStatsFormatter for Markdown {
fn format(&self, host_stats: HashMap<String, HostStats>) -> Result<String> {
let markdown = MarkdownHostStats(host_stats);
Ok(markdown.to_string())
}
}
27 changes: 3 additions & 24 deletions lychee-bin/src/formatters/host_stats/mod.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,7 @@
mod compact;
mod detailed;
mod json;
mod markdown;

pub(crate) use compact::Compact;
pub(crate) use detailed::Detailed;
pub(crate) use json::Json;
pub(crate) use markdown::Markdown;

use anyhow::Result;
use lychee_lib::ratelimit::HostStats;
use std::collections::HashMap;

/// Trait for formatting per-host statistics in different output formats
pub(crate) trait HostStatsFormatter {
/// Format the host statistics and return them as a string
fn format(&self, host_stats: HashMap<String, HostStats>) -> Result<String>;
}

/// Sort host statistics by request count (descending order)
/// This matches the display order we want in the output
fn sort_host_stats(host_stats: &HashMap<String, HostStats>) -> Vec<(&String, &HostStats)> {
let mut sorted_hosts: Vec<_> = host_stats.iter().collect();
// Sort by total requests (descending)
sorted_hosts.sort_by_key(|(_, stats)| std::cmp::Reverse(stats.total_requests));
sorted_hosts
}
pub(crate) use compact::CompactHostStats;
pub(crate) use detailed::DetailedHostStats;
pub(crate) use markdown::MarkdownHostStats;
15 changes: 1 addition & 14 deletions lychee-bin/src/formatters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub(crate) mod response;
pub(crate) mod stats;
pub(crate) mod suggestion;

use self::{host_stats::HostStatsFormatter, response::ResponseFormatter, stats::StatsFormatter};
use self::{response::ResponseFormatter, stats::StatsFormatter};
use crate::options::{OutputMode, StatsFormat};
use supports_color::Stream;

Expand Down Expand Up @@ -42,19 +42,6 @@ pub(crate) fn get_progress_formatter(mode: &OutputMode) -> Box<dyn ResponseForma
get_response_formatter(&mode)
}

/// Create a host stats formatter based on the given format and mode options
pub(crate) fn get_host_stats_formatter(
format: &StatsFormat,
_mode: &OutputMode,
) -> Box<dyn HostStatsFormatter> {
match format {
StatsFormat::Compact | StatsFormat::Raw => Box::new(host_stats::Compact::new()), // Use compact for raw
Comment thread
mre marked this conversation as resolved.
StatsFormat::Detailed => Box::new(host_stats::Detailed::new()),
StatsFormat::Json => Box::new(host_stats::Json::new()),
StatsFormat::Markdown => Box::new(host_stats::Markdown::new()),
}
}

/// Create a response formatter based on the given format option
pub(crate) fn get_response_formatter(mode: &OutputMode) -> Box<dyn ResponseFormatter> {
// Checks if color is supported in current environment or NO_COLOR is set (https://no-color.org)
Expand Down
34 changes: 24 additions & 10 deletions lychee-bin/src/formatters/stats/compact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ use std::{
time::Duration,
};

use crate::formatters::color::{BOLD_GREEN, BOLD_PINK, BOLD_YELLOW, DIM, NORMAL, color};
use crate::{formatters::get_response_formatter, options, stats::ResponseStats};
use crate::formatters::{
color::{BOLD_GREEN, BOLD_PINK, BOLD_YELLOW, DIM, NORMAL, color},
get_response_formatter,
host_stats::CompactHostStats,
stats::{OutputStats, ResponseStats},
};
use crate::options;

use super::StatsFormatter;

Expand Down Expand Up @@ -107,19 +112,23 @@ impl Compact {
}

impl StatsFormatter for Compact {
fn format(&self, stats: ResponseStats) -> Result<Option<String>> {
let compact = CompactResponseStats {
stats,
fn format(&self, stats: OutputStats) -> Result<String> {
let host_stats = CompactHostStats {
host_stats: stats.host_stats,
};
let response_stats = CompactResponseStats {
stats: stats.response_stats,
mode: self.mode.clone(),
};
Ok(Some(compact.to_string()))

Ok(format!("{response_stats}\n{host_stats}"))
}
}

#[cfg(test)]
mod tests {
use crate::formatters::stats::StatsFormatter;
use crate::{options::OutputMode, stats::ResponseStats};
use crate::formatters::stats::{ResponseStats, StatsFormatter};
use crate::options::OutputMode;
use http::StatusCode;
use lychee_lib::{InputSource, ResponseBody, Status, Uri};
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -154,7 +163,7 @@ mod tests {
let source = InputSource::RemoteUrl(Box::new(Url::parse("https://example.com").unwrap()));
error_map.insert(source, HashSet::from_iter(vec![err1, err2]));

let stats = ResponseStats {
let response_stats = ResponseStats {
total: 1,
successful: 1,
errors: 2,
Expand All @@ -175,7 +184,12 @@ mod tests {

let formatter = Compact::new(OutputMode::Plain);

let result = formatter.format(stats).unwrap().unwrap();
let result = formatter
.format(OutputStats {
response_stats,
..Default::default()
})
.unwrap();

println!("{result}");

Expand Down
Loading