|
| 1 | +use lychee_lib::{CacheStatus, ResponseBody, Status}; |
| 2 | + |
| 3 | +use super::color::{DIM, GREEN, NORMAL, PINK, YELLOW}; |
| 4 | + |
| 5 | +/// A trait for formatting a response body |
| 6 | +/// |
| 7 | +/// This trait is used to format a response body into a string. |
| 8 | +/// It can be implemented for different formatting styles such as |
| 9 | +/// colorized output or plain text. |
| 10 | +pub(crate) trait ResponseBodyFormatter: Send + Sync { |
| 11 | + fn format_response(&self, body: &ResponseBody) -> String; |
| 12 | +} |
| 13 | + |
| 14 | +/// A basic formatter that just returns the response body as a string |
| 15 | +/// without any color codes or other formatting. |
| 16 | +/// |
| 17 | +/// Under the hood, it calls the `Display` implementation of the `ResponseBody` |
| 18 | +/// type. |
| 19 | +/// |
| 20 | +/// This formatter is used when the user has requested raw output |
| 21 | +/// or when the terminal does not support color. |
| 22 | +pub(crate) struct PlainFormatter; |
| 23 | + |
| 24 | +impl ResponseBodyFormatter for PlainFormatter { |
| 25 | + fn format_response(&self, body: &ResponseBody) -> String { |
| 26 | + body.to_string() |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +/// A colorized formatter for the response body |
| 31 | +/// |
| 32 | +/// This formatter is used when the terminal supports color and the user |
| 33 | +/// has not explicitly requested raw, uncolored output. |
| 34 | +pub(crate) struct ColorFormatter; |
| 35 | + |
| 36 | +impl ResponseBodyFormatter for ColorFormatter { |
| 37 | + fn format_response(&self, body: &ResponseBody) -> String { |
| 38 | + // Determine the color based on the status. |
| 39 | + let status_color = match body.status { |
| 40 | + Status::Ok(_) | Status::Cached(CacheStatus::Ok(_)) => &GREEN, |
| 41 | + Status::Excluded |
| 42 | + | Status::Unsupported(_) |
| 43 | + | Status::Cached(CacheStatus::Excluded | CacheStatus::Unsupported) => &DIM, |
| 44 | + Status::Redirected(_) => &NORMAL, |
| 45 | + Status::UnknownStatusCode(_) | Status::Timeout(_) => &YELLOW, |
| 46 | + Status::Error(_) | Status::Cached(CacheStatus::Error(_)) => &PINK, |
| 47 | + }; |
| 48 | + |
| 49 | + let status_formatted = format_status(&body.status); |
| 50 | + |
| 51 | + let colored_status = status_color.apply_to(status_formatted); |
| 52 | + |
| 53 | + // Construct the output. |
| 54 | + format!("{} {}", colored_status, body.uri) |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +/// Desired total width of formatted string for color formatter |
| 59 | +/// |
| 60 | +/// The longest string, which needs to be formatted, is currently `[Excluded]` |
| 61 | +/// which is 10 characters long (including brackets). |
| 62 | +/// |
| 63 | +/// Keep in sync with `Status::code_as_string`, which converts status codes to |
| 64 | +/// strings. |
| 65 | +const STATUS_CODE_PADDING: usize = 10; |
| 66 | + |
| 67 | +/// Format the status code or text for the color formatter. |
| 68 | +/// |
| 69 | +/// Numeric status codes are right-aligned. |
| 70 | +/// Textual statuses are left-aligned. |
| 71 | +/// Padding is taken into account. |
| 72 | +fn format_status(status: &Status) -> String { |
| 73 | + let status_code_or_text = status.code_as_string(); |
| 74 | + |
| 75 | + // Calculate the effective padding. Ensure it's non-negative to avoid panic. |
| 76 | + let padding = STATUS_CODE_PADDING.saturating_sub(status_code_or_text.len() + 2); // +2 for brackets |
| 77 | + |
| 78 | + format!( |
| 79 | + "{}[{:>width$}]", |
| 80 | + " ".repeat(padding), |
| 81 | + status_code_or_text, |
| 82 | + width = status_code_or_text.len() |
| 83 | + ) |
| 84 | +} |
| 85 | + |
| 86 | +/// An emoji formatter for the response body |
| 87 | +/// |
| 88 | +/// This formatter replaces certain textual elements with emojis for a more |
| 89 | +/// visual output. |
| 90 | +pub(crate) struct EmojiFormatter; |
| 91 | + |
| 92 | +impl ResponseBodyFormatter for EmojiFormatter { |
| 93 | + fn format_response(&self, body: &ResponseBody) -> String { |
| 94 | + let emoji = match body.status { |
| 95 | + Status::Ok(_) | Status::Cached(CacheStatus::Ok(_)) => "✅", |
| 96 | + Status::Excluded |
| 97 | + | Status::Unsupported(_) |
| 98 | + | Status::Cached(CacheStatus::Excluded | CacheStatus::Unsupported) => "🚫", |
| 99 | + Status::Redirected(_) => "↪️", |
| 100 | + Status::UnknownStatusCode(_) | Status::Timeout(_) => "⚠️", |
| 101 | + Status::Error(_) | Status::Cached(CacheStatus::Error(_)) => "❌", |
| 102 | + }; |
| 103 | + format!("{} {}", emoji, body.uri) |
| 104 | + } |
| 105 | +} |
0 commit comments