aoc_main/
utils.rs

1//! General purpose utilities.
2
3use colored::*;
4
5use std::cmp::min;
6use std::fmt;
7use std::time::Duration;
8
9// ---
10// --- TryUnwrap: wrapper for Option and Result
11// ---
12
13pub trait TryUnwrap {
14    type Val;
15    fn try_unwrap(self) -> Result<Self::Val, String>;
16}
17
18impl<T> TryUnwrap for Option<T> {
19    type Val = T;
20
21    fn try_unwrap(self) -> Result<Self::Val, String> {
22        if let Some(val) = self {
23            Ok(val)
24        } else {
25            Err("empty output".to_string())
26        }
27    }
28}
29
30impl<T, E: fmt::Display> TryUnwrap for Result<T, E> {
31    type Val = T;
32
33    fn try_unwrap(self) -> Result<Self::Val, String> {
34        self.map_err(|err| format!("{}", err))
35    }
36}
37
38// ---
39// --- Line: helper struct for printing
40// ---
41
42const DEFAULT_WIDTH: usize = 30;
43
44/// Simple helper struct used to display lines with a dotted separator.
45/// For example: "line text (1.2 ms) .............. status".
46pub struct Line {
47    text: String,
48    duration: Option<Duration>,
49    state: Option<ColoredString>,
50}
51
52impl Line {
53    pub fn new(text: impl Into<String>) -> Self {
54        Self {
55            text: text.into(),
56            duration: None,
57            state: None,
58        }
59    }
60
61    pub fn with_duration(mut self, duration: Duration) -> Self {
62        self.duration = Some(duration);
63        self
64    }
65
66    pub fn with_state(mut self, state: impl Into<ColoredString>) -> Self {
67        self.state = Some(state.into());
68        self
69    }
70}
71
72impl fmt::Display for Line {
73    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74        let display_width = f.width().unwrap_or(DEFAULT_WIDTH);
75
76        let duration = self
77            .duration
78            .map(|duration| format!(" ({:.2?})", duration))
79            .unwrap_or_else(String::new);
80
81        write!(f, "{}{}", self.text, duration.bright_black())?;
82
83        if let Some(state) = &self.state {
84            let width = self.text.chars().count() + 1 + duration.chars().count();
85            let dots = display_width - min(display_width - 5, width) - 2;
86            let dots = ".".repeat(dots);
87            write!(f, " {}", dots.bright_black())?;
88
89            if state.contains('\n') {
90                for line in state.trim_matches('\n').lines() {
91                    write!(f, "\n    {}", line.bold())?;
92                }
93            } else {
94                write!(f, " {}", state.clone().bold())?;
95            }
96        }
97
98        Ok(())
99    }
100}