pretty_exec_lib/log/
syntax_highlight.rs

1pub use yansi;
2
3use super::{Log, Logger};
4use pipe_trait::Pipe;
5use shell_escape::unix::escape;
6use std::{
7    borrow::Cow,
8    ffi::OsStr,
9    fmt::{self, Display, Formatter},
10};
11use typed_builder::TypedBuilder;
12use yansi::{Color, Style};
13
14#[must_use]
15#[derive(Default, TypedBuilder)]
16pub struct SyntaxHighLight {
17    #[builder(default)]
18    pub prompt: Style,
19    #[builder(default)]
20    pub program: Style,
21    #[builder(default)]
22    pub argument: Style,
23    #[builder(default)]
24    pub short_flag: Style,
25    #[builder(default)]
26    pub long_flag: Style,
27}
28
29impl SyntaxHighLight {
30    pub fn colorless() -> Self {
31        Default::default()
32    }
33
34    pub fn colorful() -> Self {
35        SyntaxHighLight::builder()
36            .prompt(Style::default().dimmed())
37            .program(Style::new(Color::Green))
38            .short_flag(Style::new(Color::Red))
39            .long_flag(Style::new(Color::Red))
40            .build()
41    }
42}
43
44impl<'a, Prompt: ?Sized, Program: ?Sized, Arguments: ?Sized> Display
45    for Logger<'a, SyntaxHighLight, Prompt, Program, Arguments>
46where
47    &'a Prompt: AsRef<str>,
48    &'a Program: AsRef<OsStr>,
49    &'a Arguments: IntoIterator,
50    <&'a Arguments as IntoIterator>::Item: AsRef<OsStr>,
51{
52    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
53        let Logger {
54            method,
55            prompt,
56            program,
57            arguments,
58        } = self;
59
60        let prompt = prompt.as_ref();
61        if !prompt.is_empty() {
62            write!(f, "{} ", method.prompt.paint(prompt))?;
63        }
64
65        let program = program.as_ref().to_string_lossy().pipe(escape);
66        write!(f, "{}", method.program.paint(program))?;
67
68        for argument in *arguments {
69            let argument = argument.as_ref().to_string_lossy();
70            fn paint_escape(text: Cow<str>, style: Style) -> impl Display + '_ {
71                text.pipe(escape).pipe(|text| style.paint(text))
72            }
73            if argument.starts_with("--") {
74                let mut segments = argument.splitn(2, '=');
75                match (segments.next(), segments.next()) {
76                    (Some(_), None) => write!(f, " {}", paint_escape(argument, method.long_flag))?,
77                    (Some(flag), Some(val)) => write!(
78                        f,
79                        " {flag}{eq}{val}",
80                        flag = paint_escape(flag.into(), method.long_flag),
81                        eq = method.argument.paint("="),
82                        val = paint_escape(val.into(), method.argument),
83                    )?,
84                    _ => unreachable!(),
85                }
86            } else if argument.starts_with('-') {
87                write!(f, " {}", paint_escape(argument, method.short_flag))?
88            } else {
89                write!(f, " {}", paint_escape(argument, method.argument))?
90            };
91        }
92
93        Ok(())
94    }
95}
96
97impl<'a, Prompt: ?Sized, Program: ?Sized, Arguments: ?Sized> Log
98    for Logger<'a, SyntaxHighLight, Prompt, Program, Arguments>
99where
100    &'a Prompt: AsRef<str>,
101    &'a Program: AsRef<OsStr>,
102    &'a Arguments: IntoIterator,
103    <&'a Arguments as IntoIterator>::Item: AsRef<OsStr>,
104{
105    fn log(&self) {
106        eprintln!("{self}");
107    }
108}