1use std::fmt;
2
3use clap::builder::styling;
4use console::{style, Style, StyledObject};
5use dialoguer::theme::Theme;
6use once_cell::sync::Lazy;
7
8pub static TICK_CHARS_BRAILLE_4_6_DOWN: Lazy<String> = Lazy::new(|| String::from("⠶⢲⣰⣤⣆⡖"));
9pub static TICK_CHARS_BRAILLE_4_6_UP: Lazy<String> = Lazy::new(|| String::from("⠛⠹⠼⠶⠧⠏"));
10pub static BRAILLE_6: Lazy<String> = Lazy::new(|| String::from("⠿"));
11
12pub static THIN_PROGRESS: Lazy<String> = Lazy::new(|| String::from("━>-"));
13pub static THIN_DUAL_PROGRESS: Lazy<String> = Lazy::new(|| String::from("=>-"));
14
15pub static DOTS_4: Lazy<String> = Lazy::new(|| String::from("::"));
16
17pub fn white<D>(value: D) -> StyledObject<D> {
23 style(value).white()
24}
25
26pub fn white_b<D>(value: D) -> StyledObject<D> {
27 white(value).bold()
28}
29
30pub fn white_bi<D>(value: D) -> StyledObject<D> {
31 white_b(value).italic()
32}
33
34pub fn cyan<D>(value: D) -> StyledObject<D> {
35 style(value).cyan()
36}
37
38pub fn yellow<D>(value: D) -> StyledObject<D> {
39 style(value).yellow()
40}
41
42pub struct DialogTheme {
43 pub defaults_style: Style,
45 pub prompt_style: Style,
47 pub prompt_prefix: StyledObject<String>,
49 pub prompt_suffix: StyledObject<String>,
51 pub success_prefix: StyledObject<String>,
53 pub success_suffix: StyledObject<String>,
55 pub error_prefix: StyledObject<String>,
57 pub error_style: Style,
59 pub hint_style: Style,
61 pub values_style: Style,
63}
64
65impl Default for DialogTheme {
66 fn default() -> DialogTheme {
67 DialogTheme {
68 defaults_style: Style::new().for_stderr().cyan(),
69 prompt_style: Style::new().for_stderr().bold(),
70 prompt_prefix: style("?".to_string()).for_stderr().yellow(),
71 prompt_suffix: style("›".to_string()).for_stderr().black().bright(),
72 success_prefix: style("✔".to_string()).for_stderr().green(),
73 success_suffix: style("·".to_string()).for_stderr().black().bright(),
74 error_prefix: style("✘".to_string()).for_stderr().red(),
75 error_style: Style::new().for_stderr().red(),
76 hint_style: Style::new().for_stderr().black().bright(),
77 values_style: Style::new().for_stderr().green(),
78 }
79 }
80}
81
82impl Theme for DialogTheme {
83 fn format_confirm_prompt(
85 &self,
86 f: &mut dyn fmt::Write,
87 prompt: &str,
88 default: Option<bool>,
89 ) -> fmt::Result {
90 if !prompt.is_empty() {
91 write!(
92 f,
93 "{} {} ",
94 &self.prompt_prefix,
95 self.prompt_style.apply_to(prompt)
96 )?;
97 }
98
99 match default {
100 None => write!(
101 f,
102 "{} {}",
103 self.hint_style.apply_to("(y/n)"),
104 &self.prompt_suffix
105 ),
106 Some(true) => write!(
107 f,
108 "{} {} {}",
109 self.hint_style.apply_to("(y/n)"),
110 &self.prompt_suffix,
111 self.defaults_style.apply_to("yes")
112 ),
113 Some(false) => write!(
114 f,
115 "{} {} {}",
116 self.hint_style.apply_to("(y/n)"),
117 &self.prompt_suffix,
118 self.defaults_style.apply_to("no")
119 ),
120 }
121 }
122
123 fn format_confirm_prompt_selection(
125 &self,
126 f: &mut dyn fmt::Write,
127 prompt: &str,
128 selection: Option<bool>,
129 ) -> fmt::Result {
130 let selection = selection.map(|b| if b { "yes" } else { "no" });
131 let prefix = match selection {
132 Some("yes") => &self.success_prefix,
133 _ => &self.error_prefix,
134 };
135 let style = match selection {
136 Some("yes") => &self.values_style,
137 _ => &self.error_style,
138 };
139
140 if !prompt.is_empty() {
141 write!(f, "{} {} ", prefix, self.prompt_style.apply_to(prompt))?;
142 }
143
144 match selection {
145 Some(selection) => {
146 write!(f, "{}", style.apply_to(selection))
147 }
148 None => {
149 write!(f, "{}", &self.success_suffix)
150 }
151 }
152 }
153}
154
155pub fn clap_styles() -> styling::Styles {
156 styling::Styles::styled()
157 .header(styling::AnsiColor::Green.on_default() | styling::Effects::BOLD)
158 .usage(styling::AnsiColor::Green.on_default() | styling::Effects::BOLD)
159 .literal(styling::AnsiColor::Blue.on_default() | styling::Effects::BOLD)
160 .placeholder(styling::AnsiColor::Cyan.on_default())
161}