pretty_exec_lib/app/
args.rs

1pub mod when;
2
3pub use when::When;
4
5use super::super::{error::Error, log::syntax_highlight::SyntaxHighLight};
6use super::Param;
7use clap::{Parser, ValueHint};
8use is_terminal::IsTerminal;
9use std::{ffi::OsString, io::stderr};
10
11#[derive(Parser)]
12#[clap(name = "pretty-exec", rename_all = "kebab", version)]
13pub struct Args {
14    /// Command to execute
15    #[clap(name = "command", value_hint = ValueHint::CommandWithArguments, trailing_var_arg = true)]
16    command: Vec<OsString>,
17
18    /// Customize the prompt before the command.
19    #[clap(long, name = "prompt", default_value = "$")]
20    prompt: String,
21
22    /// Do not execute, print command only
23    #[clap(long)]
24    skip_exec: bool,
25
26    /// When to use color
27    #[clap(long, name = "color", value_enum, default_value_t = When::Auto)]
28    color: When,
29
30    /// Enable GitHub Action grouping
31    #[clap(long)]
32    github_actions: bool,
33}
34
35impl Args {
36    pub fn syntax_highlight(&self) -> SyntaxHighLight {
37        ColorMode::new(
38            self.color,
39            || self.github_actions,
40            || stderr().is_terminal(),
41        )
42        .syntax_highlight()
43    }
44
45    pub fn param(&'_ self) -> Result<Param<'_>, Error> {
46        if self.command.is_empty() {
47            return Err(Error::ProgramNotSpecified);
48        }
49
50        Ok(Param {
51            program: self.command[0].as_os_str(),
52            arguments: &self.command[1..],
53            prompt: self.prompt.as_str(),
54            skip_exec: self.skip_exec,
55            support_github_action: self.github_actions,
56            syntax_highlight: self.syntax_highlight(),
57        })
58    }
59}
60
61#[must_use]
62#[derive(Clone, Copy)]
63#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
64enum ColorMode {
65    Colorless,
66    Colorful,
67}
68
69impl ColorMode {
70    fn new(
71        color: When,
72        github_actions: impl FnOnce() -> bool,
73        is_terminal: impl FnOnce() -> bool,
74    ) -> Self {
75        match color {
76            When::Never => return ColorMode::Colorless,
77            When::Always => return ColorMode::Colorful,
78            When::Auto => {}
79        }
80
81        if github_actions() || is_terminal() {
82            return ColorMode::Colorful;
83        }
84
85        ColorMode::Colorless
86    }
87
88    fn syntax_highlight(self) -> SyntaxHighLight {
89        match self {
90            ColorMode::Colorless => SyntaxHighLight::colorless(),
91            ColorMode::Colorful => SyntaxHighLight::colorful(),
92        }
93    }
94}
95
96#[cfg(test)]
97mod test_color_mode {
98    use super::{ColorMode, When};
99    use itertools::Itertools;
100    use maplit::btreemap;
101    use pretty_assertions::assert_eq;
102    use std::collections::BTreeMap;
103
104    #[test]
105    fn new() {
106        use ColorMode::{Colorful, Colorless};
107        use When::{Always, Auto, Never};
108
109        let color = [Auto, Never, Always];
110        let github_actions = [false, true];
111        let is_terminal = [false, true];
112
113        let received: BTreeMap<_, _> = [color.len(), github_actions.len(), is_terminal.len()]
114            .into_iter()
115            .map(|len| 0..len)
116            .multi_cartesian_product()
117            .map(|indices| (indices[0], indices[1], indices[2]))
118            .map(|(i, j, k)| (color[i], github_actions[j], is_terminal[k]))
119            .map(|(a, b, c)| ((a, b, c), ColorMode::new(a, || b, || c)))
120            .collect();
121        dbg!(&received);
122
123        let expected = btreemap! {
124            (Auto, false, false) => Colorless,
125            (Auto, true, false) => Colorful,
126            (Auto, false, true) => Colorful,
127            (Auto, true, true) => Colorful,
128
129            (Never, false, false) => Colorless,
130            (Never, true, false) => Colorless,
131            (Never, false, true) => Colorless,
132            (Never, true, true) => Colorless,
133
134            (Always, false, false) => Colorful,
135            (Always, true, false) => Colorful,
136            (Always, false, true) => Colorful,
137            (Always, true, true) => Colorful,
138        };
139
140        assert_eq!(received, expected);
141    }
142}