|
1 | 1 | use crate::config;
|
2 | 2 | use serde::{Deserialize, Serialize};
|
3 |
| -use std::{borrow::Cow, str::FromStr}; |
| 3 | +use std::{str::FromStr, time::Duration}; |
4 | 4 | use tui::{
|
5 | 5 | style::{Color, Modifier, Style},
|
6 | 6 | text::Span,
|
@@ -32,11 +32,41 @@ pub enum Palette {
|
32 | 32 | All,
|
33 | 33 | }
|
34 | 34 |
|
| 35 | +/// Represents formatted time spans. |
| 36 | +/// |
| 37 | +/// Distinguishing between different units allows appropriate colouring. |
| 38 | +enum FormattedDuration { |
| 39 | + /// Days (and no minor unit), e.g. `102d` |
| 40 | + Days(String), |
| 41 | + /// Days with hours, e.g. `12d03h` |
| 42 | + DaysHours(String), |
| 43 | + /// Hours with minutes, e.g. `14h32m` |
| 44 | + HoursMinutes(String), |
| 45 | + /// Minutes with seconds, e.g. `43m02s` |
| 46 | + MinutesSeconds(String), |
| 47 | + /// The `time::Duration` debug string which uses units ranging from |
| 48 | + /// picoseconds (`ps`) to seconds (`s`). May contain decimal digits |
| 49 | + /// (e.g. `628.76ms`) or not (e.g. `32ns`) |
| 50 | + Debug(String), |
| 51 | +} |
| 52 | + |
| 53 | +impl FormattedDuration { |
| 54 | + fn into_inner(self) -> String { |
| 55 | + match self { |
| 56 | + Self::Days(inner) => inner, |
| 57 | + Self::DaysHours(inner) => inner, |
| 58 | + Self::HoursMinutes(inner) => inner, |
| 59 | + Self::MinutesSeconds(inner) => inner, |
| 60 | + Self::Debug(inner) => inner, |
| 61 | + } |
| 62 | + } |
| 63 | +} |
| 64 | + |
35 | 65 | fn fg_style(color: Color) -> Style {
|
36 | 66 | Style::default().fg(color)
|
37 | 67 | }
|
38 | 68 |
|
39 |
| -// === impl Config === |
| 69 | +// === impl Styles === |
40 | 70 |
|
41 | 71 | impl Styles {
|
42 | 72 | pub fn from_config(config: config::ViewOptions) -> Self {
|
@@ -126,39 +156,100 @@ impl Styles {
|
126 | 156 | }
|
127 | 157 | }
|
128 | 158 |
|
129 |
| - pub fn time_units<'a>(&self, text: impl Into<Cow<'a, str>>) -> Span<'a> { |
130 |
| - let mut text = text.into(); |
131 |
| - if !self.toggles.color_durations() { |
132 |
| - return Span::raw(text); |
133 |
| - } |
| 159 | + /// Creates a span with a formatted duration inside. |
| 160 | + /// |
| 161 | + /// The formatted duration will be colored depending on the palette |
| 162 | + /// defined for this `Styles` object. |
| 163 | + /// |
| 164 | + /// If the `width` parameter is `None` then no padding will be |
| 165 | + /// added. Otherwise the text in the span will be left-padded to |
| 166 | + /// the specified width (right aligned). Passing `Some(0)` is |
| 167 | + /// equivalent to `None`. |
| 168 | + pub fn time_units<'a>(&self, dur: Duration, prec: usize, width: Option<usize>) -> Span<'a> { |
| 169 | + let formatted = self.duration_text(dur, width.unwrap_or(0), prec); |
134 | 170 |
|
135 |
| - if !self.utf8 { |
136 |
| - if let Some(mu_offset) = text.find("µs") { |
137 |
| - text.to_mut().replace_range(mu_offset.., "us"); |
138 |
| - } |
| 171 | + if !self.toggles.color_durations() { |
| 172 | + return Span::raw(formatted.into_inner()); |
139 | 173 | }
|
140 | 174 |
|
141 | 175 | let style = match self.palette {
|
142 |
| - Palette::NoColors => return Span::raw(text), |
143 |
| - Palette::Ansi8 | Palette::Ansi16 => match text.as_ref() { |
144 |
| - s if s.ends_with("ps") => fg_style(Color::Blue), |
145 |
| - s if s.ends_with("ns") => fg_style(Color::Green), |
146 |
| - s if s.ends_with("µs") || s.ends_with("us") => fg_style(Color::Yellow), |
147 |
| - s if s.ends_with("ms") => fg_style(Color::Red), |
148 |
| - s if s.ends_with('s') => fg_style(Color::Magenta), |
| 176 | + Palette::NoColors => return Span::raw(formatted.into_inner()), |
| 177 | + Palette::Ansi8 | Palette::Ansi16 => match &formatted { |
| 178 | + FormattedDuration::Days(_) => fg_style(Color::Blue), |
| 179 | + FormattedDuration::DaysHours(_) => fg_style(Color::Blue), |
| 180 | + FormattedDuration::HoursMinutes(_) => fg_style(Color::Cyan), |
| 181 | + FormattedDuration::MinutesSeconds(_) => fg_style(Color::Green), |
| 182 | + FormattedDuration::Debug(s) if s.ends_with("ps") => fg_style(Color::Gray), |
| 183 | + FormattedDuration::Debug(s) if s.ends_with("ns") => fg_style(Color::Gray), |
| 184 | + FormattedDuration::Debug(s) if s.ends_with("µs") || s.ends_with("us") => { |
| 185 | + fg_style(Color::Magenta) |
| 186 | + } |
| 187 | + FormattedDuration::Debug(s) if s.ends_with("ms") => fg_style(Color::Red), |
| 188 | + FormattedDuration::Debug(s) if s.ends_with('s') => fg_style(Color::Yellow), |
149 | 189 | _ => Style::default(),
|
150 | 190 | },
|
151 |
| - Palette::Ansi256 | Palette::All => match text.as_ref() { |
152 |
| - s if s.ends_with("ps") => fg_style(Color::Indexed(40)), // green 3 |
153 |
| - s if s.ends_with("ns") => fg_style(Color::Indexed(41)), // spring green 3 |
154 |
| - s if s.ends_with("µs") || s.ends_with("us") => fg_style(Color::Indexed(42)), // spring green 2 |
155 |
| - s if s.ends_with("ms") => fg_style(Color::Indexed(43)), // cyan 3 |
156 |
| - s if s.ends_with('s') => fg_style(Color::Indexed(44)), // dark turquoise, |
| 191 | + Palette::Ansi256 | Palette::All => match &formatted { |
| 192 | + FormattedDuration::Days(_) => fg_style(Color::Indexed(33)), // dodger blue 1 |
| 193 | + FormattedDuration::DaysHours(_) => fg_style(Color::Indexed(33)), // dodger blue 1 |
| 194 | + FormattedDuration::HoursMinutes(_) => fg_style(Color::Indexed(39)), // deep sky blue 1 |
| 195 | + FormattedDuration::MinutesSeconds(_) => fg_style(Color::Indexed(45)), // turquoise 2 |
| 196 | + FormattedDuration::Debug(s) if s.ends_with("ps") => fg_style(Color::Indexed(40)), // green 3 |
| 197 | + FormattedDuration::Debug(s) if s.ends_with("ns") => fg_style(Color::Indexed(41)), // spring green 3 |
| 198 | + FormattedDuration::Debug(s) if s.ends_with("µs") || s.ends_with("us") => { |
| 199 | + fg_style(Color::Indexed(42)) |
| 200 | + } // spring green 2 |
| 201 | + FormattedDuration::Debug(s) if s.ends_with("ms") => fg_style(Color::Indexed(43)), // cyan 3 |
| 202 | + FormattedDuration::Debug(s) if s.ends_with('s') => fg_style(Color::Indexed(44)), // dark turquoise, |
157 | 203 | _ => Style::default(),
|
158 | 204 | },
|
159 | 205 | };
|
160 | 206 |
|
161 |
| - Span::styled(text, style) |
| 207 | + Span::styled(formatted.into_inner(), style) |
| 208 | + } |
| 209 | + |
| 210 | + fn duration_text(&self, dur: Duration, width: usize, prec: usize) -> FormattedDuration { |
| 211 | + let secs = dur.as_secs(); |
| 212 | + |
| 213 | + if secs >= 60 * 60 * 24 * 100 { |
| 214 | + let days = secs / (60 * 60 * 24); |
| 215 | + FormattedDuration::Days(format!("{days:>width$}d", days = days, width = width)) |
| 216 | + } else if secs >= 60 * 60 * 24 { |
| 217 | + let hours = secs / (60 * 60); |
| 218 | + FormattedDuration::DaysHours(format!( |
| 219 | + "{days:>leading_width$}d{hours:02.0}h", |
| 220 | + days = hours / 24, |
| 221 | + hours = hours % 24, |
| 222 | + // Subtract the known 4 characters that trail the days value. |
| 223 | + leading_width = width.saturating_sub(4), |
| 224 | + )) |
| 225 | + } else if secs >= 60 * 60 { |
| 226 | + let mins = secs / 60; |
| 227 | + FormattedDuration::HoursMinutes(format!( |
| 228 | + "{hours:>leading_width$}h{minutes:02.0}m", |
| 229 | + hours = mins / 60, |
| 230 | + minutes = mins % 60, |
| 231 | + // Subtract the known 4 characters that trail the hours value. |
| 232 | + leading_width = width.saturating_sub(4), |
| 233 | + )) |
| 234 | + } else if secs >= 60 { |
| 235 | + FormattedDuration::MinutesSeconds(format!( |
| 236 | + "{minutes:>leading_width$}m{seconds:02.0}s", |
| 237 | + minutes = secs / 60, |
| 238 | + seconds = secs % 60, |
| 239 | + // Subtract the known 4 characters that trail the minutes value. |
| 240 | + leading_width = width.saturating_sub(4), |
| 241 | + )) |
| 242 | + } else { |
| 243 | + let mut text = format!("{:>width$.prec$?}", dur, width = width, prec = prec); |
| 244 | + |
| 245 | + if !self.utf8 { |
| 246 | + if let Some(mu_offset) = text.find("µs") { |
| 247 | + text.replace_range(mu_offset.., "us"); |
| 248 | + } |
| 249 | + } |
| 250 | + |
| 251 | + FormattedDuration::Debug(text) |
| 252 | + } |
162 | 253 | }
|
163 | 254 |
|
164 | 255 | pub fn terminated(&self) -> Style {
|
|
0 commit comments