1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
pub mod when;

pub use when::When;

use super::super::{error::Error, log::syntax_highlight::SyntaxHighLight};
use super::Param;
use clap::{Parser, ValueHint};
use is_terminal::IsTerminal;
use std::{ffi::OsString, io::stderr};

#[derive(Parser)]
#[clap(name = "pretty-exec", rename_all = "kebab", version)]
pub struct Args {
    /// Command to execute
    #[clap(name = "command", value_hint = ValueHint::CommandWithArguments, trailing_var_arg = true)]
    command: Vec<OsString>,

    /// Customize the prompt before the command.
    #[clap(long, name = "prompt", default_value = "$")]
    prompt: String,

    /// Do not execute, print command only
    #[clap(long)]
    skip_exec: bool,

    /// When to use color
    #[clap(long, name = "color", value_enum, default_value_t = When::Auto)]
    color: When,

    /// Enable GitHub Action grouping
    #[clap(long)]
    github_actions: bool,
}

impl Args {
    pub fn syntax_highlight(&self) -> SyntaxHighLight {
        ColorMode::new(
            self.color,
            || self.github_actions,
            || stderr().is_terminal(),
        )
        .syntax_highlight()
    }

    pub fn param(&'_ self) -> Result<Param<'_>, Error> {
        if self.command.is_empty() {
            return Err(Error::MissingProgram);
        }

        Ok(Param {
            program: self.command[0].as_os_str(),
            arguments: &self.command[1..],
            prompt: self.prompt.as_str(),
            skip_exec: self.skip_exec,
            support_github_action: self.github_actions,
            syntax_highlight: self.syntax_highlight(),
        })
    }
}

#[must_use]
#[derive(Clone, Copy)]
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
enum ColorMode {
    Colorless,
    Colorful,
}

impl ColorMode {
    fn new(
        color: When,
        github_actions: impl FnOnce() -> bool,
        is_terminal: impl FnOnce() -> bool,
    ) -> Self {
        match color {
            When::Never => return ColorMode::Colorless,
            When::Always => return ColorMode::Colorful,
            When::Auto => {}
        }

        if github_actions() || is_terminal() {
            return ColorMode::Colorful;
        }

        ColorMode::Colorless
    }

    fn syntax_highlight(self) -> SyntaxHighLight {
        match self {
            ColorMode::Colorless => SyntaxHighLight::colorless(),
            ColorMode::Colorful => SyntaxHighLight::colorful(),
        }
    }
}

#[cfg(test)]
mod test_color_mode {
    use super::{ColorMode, When};
    use itertools::Itertools;
    use maplit::btreemap;
    use pretty_assertions::assert_eq;
    use std::collections::BTreeMap;

    #[test]
    fn new() {
        use ColorMode::{Colorful, Colorless};
        use When::{Always, Auto, Never};

        let color = [Auto, Never, Always];
        let github_actions = [false, true];
        let is_terminal = [false, true];

        let received: BTreeMap<_, _> = [color.len(), github_actions.len(), is_terminal.len()]
            .into_iter()
            .map(|len| 0..len)
            .multi_cartesian_product()
            .map(|indices| (indices[0], indices[1], indices[2]))
            .map(|(i, j, k)| (color[i], github_actions[j], is_terminal[k]))
            .map(|(a, b, c)| ((a, b, c), ColorMode::new(a, || b, || c)))
            .collect();
        dbg!(&received);

        let expected = btreemap! {
            (Auto, false, false) => Colorless,
            (Auto, true, false) => Colorful,
            (Auto, false, true) => Colorful,
            (Auto, true, true) => Colorful,

            (Never, false, false) => Colorless,
            (Never, true, false) => Colorless,
            (Never, false, true) => Colorless,
            (Never, true, true) => Colorless,

            (Always, false, false) => Colorful,
            (Always, true, false) => Colorful,
            (Always, false, true) => Colorful,
            (Always, true, true) => Colorful,
        };

        assert_eq!(received, expected);
    }
}