random_output/
modifier.rs

1use crate::args::Args;
2use crate::time;
3
4use crossterm::style::Stylize;
5use std::borrow::{Borrow, Cow};
6
7#[derive(Debug)]
8pub struct Modifier<'a> {
9    pub dates: bool,
10    pub loglevels: bool,
11    pub colors: bool,
12    pub prefix: &'a str,
13    pub suffix: &'a str,
14    pub prefix_err: Option<&'a str>,
15    pub suffix_err: Option<&'a str>,
16}
17
18impl<'a> From<&'a Args> for Modifier<'a> {
19    fn from(args: &'a Args) -> Self {
20        Modifier {
21            dates: args.with_dates,
22            loglevels: args.with_dates,
23            colors: args.with_colors,
24            prefix: &args.prefix,
25            suffix: &args.suffix,
26            prefix_err: args.prefix_err.as_deref(),
27            suffix_err: args.suffix_err.as_deref(),
28        }
29    }
30}
31
32#[derive(Debug, Eq, PartialEq, Clone, Copy)]
33pub enum Output {
34    StdOut,
35    StdErr,
36}
37
38pub fn add_modifier_to_line(line: &str, output: Output, modifier: &Modifier) -> String {
39    let prefix = match output {
40        Output::StdOut => modifier.prefix,
41        Output::StdErr => modifier.prefix_err.unwrap_or(modifier.prefix),
42    };
43
44    let suffix = match output {
45        Output::StdOut => modifier.suffix,
46        Output::StdErr => modifier.suffix_err.unwrap_or(modifier.suffix),
47    };
48
49    let date = if modifier.dates {
50        let d = format!("{}", time::now().format("[%Y-%m-%d %H:%M:%S%.3f] "));
51        Cow::Owned(if modifier.colors {
52            format!("{}", d.grey())
53        } else {
54            d
55        })
56    } else {
57        Cow::Borrowed("")
58    };
59
60    let loglevel = if modifier.loglevels {
61        match (output, modifier.colors) {
62            (Output::StdOut, true) => Cow::Owned(format!("{}", "[INFO] ".green())),
63            (Output::StdOut, false) => Cow::Borrowed("[INFO] "),
64            (Output::StdErr, true) => Cow::Owned(format!("{}", "[ERR] ".red())),
65            (Output::StdErr, false) => Cow::Borrowed("[ERR] "),
66        }
67    } else {
68        Cow::Borrowed("")
69    };
70
71    prefix.to_string() + date.borrow() + loglevel.borrow() + line + suffix
72}
73
74#[cfg(test)]
75mod test {
76    use super::*;
77    use chrono::TimeZone;
78
79    #[test]
80    fn null_modifier_does_nothing() {
81        let modifier = Modifier {
82            dates: false,
83            loglevels: false,
84            colors: false,
85            prefix: "",
86            suffix: "",
87            prefix_err: None,
88            suffix_err: None,
89        };
90
91        for output in [Output::StdOut, Output::StdErr] {
92            assert_eq!(
93                add_modifier_to_line("hogehoge", output, &modifier),
94                "hogehoge"
95            );
96        }
97    }
98
99    #[test]
100    fn prefix_and_suffix() {
101        let modifier = Modifier {
102            dates: false,
103            loglevels: false,
104            colors: false,
105            prefix: "prefix",
106            suffix: "suffix",
107            prefix_err: Some("prefix-err"),
108            suffix_err: Some("suffix-err"),
109        };
110
111        assert_eq!(
112            add_modifier_to_line("hoge", Output::StdOut, &modifier),
113            "prefixhogesuffix"
114        );
115        assert_eq!(
116            add_modifier_to_line("hoge", Output::StdErr, &modifier),
117            "prefix-errhogesuffix-err"
118        );
119    }
120
121    #[test]
122    fn prefix_err_and_suffix_err_defaults_to_prefix_and_suffix() {
123        let modifier = Modifier {
124            dates: false,
125            loglevels: false,
126            colors: false,
127            prefix: "prefix",
128            suffix: "suffix",
129            prefix_err: None,
130            suffix_err: None,
131        };
132
133        assert_eq!(
134            add_modifier_to_line("hoge", Output::StdErr, &modifier),
135            "prefixhogesuffix"
136        );
137    }
138
139    #[test]
140    fn loglevels() {
141        let modifier = Modifier {
142            dates: false,
143            loglevels: true,
144            colors: false,
145            prefix: "",
146            suffix: "",
147            prefix_err: None,
148            suffix_err: None,
149        };
150
151        assert_eq!(
152            add_modifier_to_line("hoge", Output::StdOut, &modifier),
153            "[INFO] hoge"
154        );
155        assert_eq!(
156            add_modifier_to_line("hoge", Output::StdErr, &modifier),
157            "[ERR] hoge"
158        );
159    }
160
161    #[test]
162    fn prefix_dates_loglevels_come_in_order() {
163        let dt: chrono::NaiveDateTime = "2022-09-02T10:11:12".parse().unwrap();
164        let ldt = chrono::Local.from_local_datetime(&dt).unwrap();
165        time::mock_time::set_mock_time(ldt);
166
167        let modifier = Modifier {
168            dates: true,
169            loglevels: true,
170            colors: false,
171            prefix: "prefix",
172            suffix: "",
173            prefix_err: Some("prefix"),
174            suffix_err: None,
175        };
176
177        assert_eq!(
178            add_modifier_to_line("hoge", Output::StdOut, &modifier),
179            "prefix[2022-09-02 10:11:12.000] [INFO] hoge"
180        );
181        assert_eq!(
182            add_modifier_to_line("hoge", Output::StdErr, &modifier),
183            "prefix[2022-09-02 10:11:12.000] [ERR] hoge"
184        );
185    }
186}