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 #[clap(name = "command", value_hint = ValueHint::CommandWithArguments, trailing_var_arg = true)]
16 command: Vec<OsString>,
17
18 #[clap(long, name = "prompt", default_value = "$")]
20 prompt: String,
21
22 #[clap(long)]
24 skip_exec: bool,
25
26 #[clap(long, name = "color", value_enum, default_value_t = When::Auto)]
28 color: When,
29
30 #[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}