create_farm/utils/
theme.rs

1use std::fmt;
2
3use dialoguer::{
4  console::{style, Style, StyledObject},
5  theme::Theme,
6};
7
8pub struct ColorfulTheme {
9  /// The style for default values
10  pub defaults_style: Style,
11  /// The style for prompt
12  pub prompt_style: Style,
13  /// Prompt prefix value and style
14  pub prompt_prefix: StyledObject<String>,
15  /// Prompt suffix value and style
16  pub prompt_suffix: StyledObject<String>,
17  /// Prompt on success prefix value and style
18  pub success_prefix: StyledObject<String>,
19  /// Prompt on success suffix value and style
20  pub success_suffix: StyledObject<String>,
21  /// Error prefix value and style
22  pub error_prefix: StyledObject<String>,
23  /// The style for error message
24  pub error_style: Style,
25  /// The style for hints
26  pub hint_style: Style,
27  /// The style for values on prompt success
28  pub values_style: Style,
29  /// The style for active items
30  pub active_item_style: Style,
31  /// The style for inactive items
32  pub inactive_item_style: Style,
33  /// Active item in select prefix value and style
34  pub active_item_prefix: StyledObject<String>,
35  /// Inctive item in select prefix value and style
36  pub inactive_item_prefix: StyledObject<String>,
37  /// Checked item in multi select prefix value and style
38  pub checked_item_prefix: StyledObject<String>,
39  /// Unchecked item in multi select prefix value and style
40  pub unchecked_item_prefix: StyledObject<String>,
41  /// Picked item in sort prefix value and style
42  pub picked_item_prefix: StyledObject<String>,
43  /// Unpicked item in sort prefix value and style
44  pub unpicked_item_prefix: StyledObject<String>,
45  /// Show the selections from certain prompts inline
46  pub inline_selections: bool,
47}
48
49impl Default for ColorfulTheme {
50  fn default() -> ColorfulTheme {
51    ColorfulTheme {
52      defaults_style: Style::new().for_stderr().cyan(),
53      prompt_style: Style::new().for_stderr().bold(),
54      prompt_prefix: style("?".to_string()).for_stderr().yellow(),
55      prompt_suffix: style("›".to_string()).for_stderr().black().bright(),
56      success_prefix: style("✔".to_string()).for_stderr().green(),
57      success_suffix: style("·".to_string()).for_stderr().black().bright(),
58      error_prefix: style("✘".to_string()).for_stderr().red(),
59      error_style: Style::new().for_stderr().red(),
60      hint_style: Style::new().for_stderr().black().bright(),
61      values_style: Style::new().for_stderr().green(),
62      active_item_style: Style::new().for_stderr().cyan(),
63      inactive_item_style: Style::new().for_stderr(),
64      active_item_prefix: style("❯".to_string()).for_stderr().cyan(),
65      inactive_item_prefix: style(" ".to_string()).for_stderr(),
66      checked_item_prefix: style("✔".to_string()).for_stderr().green(),
67      unchecked_item_prefix: style("✔".to_string()).for_stderr().black(),
68      picked_item_prefix: style("❯".to_string()).for_stderr().green(),
69      unpicked_item_prefix: style(" ".to_string()).for_stderr(),
70      inline_selections: true,
71    }
72  }
73}
74
75impl Theme for ColorfulTheme {
76  /// Formats a prompt.
77  fn format_prompt(&self, f: &mut dyn fmt::Write, prompt: &str) -> fmt::Result {
78    if !prompt.is_empty() {
79      write!(
80        f,
81        "{} {} ",
82        &self.prompt_prefix,
83        self.prompt_style.apply_to(prompt)
84      )?;
85    }
86
87    write!(f, "{}", &self.prompt_suffix)
88  }
89
90  /// Formats an error
91  fn format_error(&self, f: &mut dyn fmt::Write, err: &str) -> fmt::Result {
92    write!(
93      f,
94      "{} {}",
95      &self.error_prefix,
96      self.error_style.apply_to(err)
97    )
98  }
99
100  /// Formats an input prompt.
101  fn format_input_prompt(
102    &self,
103    f: &mut dyn fmt::Write,
104    prompt: &str,
105    default: Option<&str>,
106  ) -> fmt::Result {
107    if !prompt.is_empty() {
108      write!(
109        f,
110        "{} {} ",
111        &self.prompt_prefix,
112        self.prompt_style.apply_to(prompt)
113      )?;
114    }
115
116    match default {
117      Some(default) => write!(
118        f,
119        "{} {} ",
120        self.hint_style.apply_to(&format!("({default})")),
121        &self.prompt_suffix
122      ),
123      None => write!(f, "{} ", &self.prompt_suffix),
124    }
125  }
126
127  /// Formats a confirm prompt.
128  fn format_confirm_prompt(
129    &self,
130    f: &mut dyn fmt::Write,
131    prompt: &str,
132    default: Option<bool>,
133  ) -> fmt::Result {
134    if !prompt.is_empty() {
135      write!(
136        f,
137        "{} {} ",
138        &self.prompt_prefix,
139        self.prompt_style.apply_to(prompt)
140      )?;
141    }
142
143    match default {
144      None => write!(
145        f,
146        "{} {}",
147        self.hint_style.apply_to("(y/n)"),
148        &self.prompt_suffix
149      ),
150      Some(true) => write!(
151        f,
152        "{} {} {}",
153        self.hint_style.apply_to("(y/n)"),
154        &self.prompt_suffix,
155        self.defaults_style.apply_to("yes")
156      ),
157      Some(false) => write!(
158        f,
159        "{} {} {}",
160        self.hint_style.apply_to("(y/n)"),
161        &self.prompt_suffix,
162        self.defaults_style.apply_to("no")
163      ),
164    }
165  }
166
167  /// Formats a confirm prompt after selection.
168  fn format_confirm_prompt_selection(
169    &self,
170    f: &mut dyn fmt::Write,
171    prompt: &str,
172    selection: Option<bool>,
173  ) -> fmt::Result {
174    if !prompt.is_empty() {
175      write!(
176        f,
177        "{} {} ",
178        &self.success_prefix,
179        self.prompt_style.apply_to(prompt)
180      )?;
181    }
182    let selection = selection.map(|b| if b { "yes" } else { "no" });
183
184    match selection {
185      Some(selection) => {
186        write!(
187          f,
188          "{} {}",
189          &self.success_suffix,
190          self.values_style.apply_to(selection)
191        )
192      }
193      None => {
194        write!(f, "{}", &self.success_suffix)
195      }
196    }
197  }
198
199  /// Formats an input prompt after selection.
200  fn format_input_prompt_selection(
201    &self,
202    f: &mut dyn fmt::Write,
203    prompt: &str,
204    sel: &str,
205  ) -> fmt::Result {
206    if !prompt.is_empty() {
207      write!(
208        f,
209        "{} {} ",
210        &self.success_prefix,
211        self.prompt_style.apply_to(prompt)
212      )?;
213    }
214
215    write!(
216      f,
217      "{} {}",
218      &self.success_suffix,
219      self.values_style.apply_to(sel)
220    )
221  }
222
223  /// Formats a multi select prompt after selection.
224  fn format_multi_select_prompt_selection(
225    &self,
226    f: &mut dyn fmt::Write,
227    prompt: &str,
228    selections: &[&str],
229  ) -> fmt::Result {
230    if !prompt.is_empty() {
231      write!(
232        f,
233        "{} {} ",
234        &self.success_prefix,
235        self.prompt_style.apply_to(prompt)
236      )?;
237    }
238
239    write!(f, "{} ", &self.success_suffix)?;
240
241    if self.inline_selections {
242      for (idx, sel) in selections.iter().enumerate() {
243        write!(
244          f,
245          "{}{}",
246          if idx == 0 { "" } else { ", " },
247          self.values_style.apply_to(sel)
248        )?;
249      }
250    }
251
252    Ok(())
253  }
254
255  /// Formats a select prompt item.
256  fn format_select_prompt_item(
257    &self,
258    f: &mut dyn fmt::Write,
259    text: &str,
260    active: bool,
261  ) -> fmt::Result {
262    let (text, desc) = text.split_once('-').unwrap_or((text, ""));
263
264    if active {
265      write!(
266        f,
267        "{} {}",
268        self.active_item_prefix,
269        self.active_item_style.apply_to(&format!("{}{}", 
270          text.trim(),
271          if !desc.is_empty() {
272            format!(" - {}", desc.trim())
273          } else {
274            String::new()
275          }
276        ))
277      )
278    } else {
279      write!(
280        f,
281        "{} {}{}",
282        self.inactive_item_prefix,
283        self.inactive_item_style.apply_to(text.trim()),
284        if !desc.is_empty() {
285          format!(" - {}", self.hint_style.apply_to(desc.trim()))
286        } else {
287          String::new()
288        }
289      )
290    }
291  }
292
293  /// Formats a multi select prompt item.
294  fn format_multi_select_prompt_item(
295    &self,
296    f: &mut dyn fmt::Write,
297    text: &str,
298    checked: bool,
299    active: bool,
300  ) -> fmt::Result {
301    let details = match (checked, active) {
302      (true, true) => (
303        &self.checked_item_prefix,
304        self.active_item_style.apply_to(text),
305      ),
306      (true, false) => (
307        &self.checked_item_prefix,
308        self.inactive_item_style.apply_to(text),
309      ),
310      (false, true) => (
311        &self.unchecked_item_prefix,
312        self.active_item_style.apply_to(text),
313      ),
314      (false, false) => (
315        &self.unchecked_item_prefix,
316        self.inactive_item_style.apply_to(text),
317      ),
318    };
319
320    write!(f, "{} {}", details.0, details.1)
321  }
322
323  /// Formats a sort prompt item.
324  fn format_sort_prompt_item(
325    &self,
326    f: &mut dyn fmt::Write,
327    text: &str,
328    picked: bool,
329    active: bool,
330  ) -> fmt::Result {
331    let details = match (picked, active) {
332      (true, true) => (
333        &self.picked_item_prefix,
334        self.active_item_style.apply_to(text),
335      ),
336      (false, true) => (
337        &self.unpicked_item_prefix,
338        self.active_item_style.apply_to(text),
339      ),
340      (_, false) => (
341        &self.unpicked_item_prefix,
342        self.inactive_item_style.apply_to(text),
343      ),
344    };
345
346    write!(f, "{} {}", details.0, details.1)
347  }
348}