1use owo_colors::{OwoColorize, Stream};
2
3use crate::path_display;
4
5fn symbol_for_stream(s: &str, stream: Stream) -> String {
6 match s {
7 "✓" => s.if_supports_color(stream, |t| t.green()).to_string(),
8 "↑" => s.if_supports_color(stream, |t| t.cyan()).to_string(),
9 "~" => s.if_supports_color(stream, |t| t.yellow()).to_string(),
10 "!" => s.if_supports_color(stream, |t| t.magenta()).to_string(),
11 "✗" => s.if_supports_color(stream, |t| t.red()).to_string(),
12 other => other.to_string(),
13 }
14}
15
16pub fn symbol(s: &str) -> String {
17 symbol_for_stream(s, Stream::Stdout)
18}
19
20pub fn symbol_stderr(s: &str) -> String {
26 symbol_for_stream(s, Stream::Stderr)
27}
28
29pub fn green(s: &str) -> String {
30 s.if_supports_color(Stream::Stdout, |t| t.green())
31 .to_string()
32}
33
34pub fn yellow(s: &str) -> String {
35 s.if_supports_color(Stream::Stdout, |t| t.yellow())
36 .to_string()
37}
38
39pub fn yellow_stderr(s: &str) -> String {
42 s.if_supports_color(Stream::Stderr, |t| t.yellow())
43 .to_string()
44}
45
46pub fn red(s: &str) -> String {
47 s.if_supports_color(Stream::Stdout, |t| t.red()).to_string()
48}
49
50pub fn bold(s: &str) -> String {
51 s.if_supports_color(Stream::Stdout, |t| t.bold())
52 .to_string()
53}
54
55pub fn dim(s: &str) -> String {
56 s.if_supports_color(Stream::Stdout, |t| t.dimmed())
57 .to_string()
58}
59
60pub fn cyan(s: &str) -> String {
61 s.if_supports_color(Stream::Stdout, |t| t.cyan())
62 .to_string()
63}
64
65pub fn status_label(s: &str, sym: &str) -> String {
66 match sym {
67 "✓" => s
68 .if_supports_color(Stream::Stdout, |t| t.green())
69 .to_string(),
70 "↑" => s
71 .if_supports_color(Stream::Stdout, |t| t.cyan())
72 .to_string(),
73 "~" | "!" => s
74 .if_supports_color(Stream::Stdout, |t| t.yellow())
75 .to_string(),
76 "✗" => s.if_supports_color(Stream::Stdout, |t| t.red()).to_string(),
77 _ => s.to_string(),
78 }
79}
80
81const PRESETS_NOTE_LABEL_WIDTH: usize = 18; fn presets_note_value(plain_label: &str, styled_label: &str, value: &str) -> String {
85 let pad = " ".repeat(PRESETS_NOTE_LABEL_WIDTH.saturating_sub(plain_label.chars().count()) + 2);
86 format!("{styled_label}{pad}{value}")
87}
88
89fn presets_note(plain_label: &str, styled_label: &str, dir: &std::path::Path) -> String {
90 presets_note_value(plain_label, styled_label, &path_display::format(dir))
91}
92
93pub fn external_presets_note(dir: &std::path::Path) -> String {
95 use owo_colors::Style;
96 let label = "◈ External Presets";
97 let styled = label
98 .if_supports_color(Stream::Stdout, |t| t.style(Style::new().bold().cyan()))
99 .to_string();
100 presets_note(label, &styled, dir)
101}
102
103pub fn presets_overlay_note(dir: &std::path::Path) -> String {
105 use owo_colors::Style;
106 let label = "◈ Presets Overlay";
107 let styled = label
108 .if_supports_color(Stream::Stdout, |t| t.style(Style::new().bold().yellow()))
109 .to_string();
110 presets_note(label, &styled, dir)
111}
112
113pub fn shell_deployment_note(value: &str) -> String {
115 let label = "◈ Shell Deployment";
116 presets_note_value(label, label, value)
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use std::path::Path;
123
124 #[test]
125 fn external_presets_note_contains_path() {
126 let path = Path::new("/home/user/.custom/presets");
127 let note = external_presets_note(path);
128 assert!(
129 note.contains("/home/user/.custom/presets"),
130 "note should include the path: {note:?}"
131 );
132 }
133
134 #[test]
135 fn external_presets_note_contains_presets_label() {
136 let path = Path::new("/some/dir");
137 let note = external_presets_note(path);
138 assert!(
139 note.contains("External Presets"),
140 "note should include the 'External Presets' label: {note:?}"
141 );
142 }
143
144 #[test]
145 fn presets_note_values_use_the_same_column() {
146 let external = external_presets_note(Path::new("/external"));
147 let overlay = presets_overlay_note(Path::new("/overlay"));
148 let deployment = shell_deployment_note("snapshot");
149
150 assert_eq!(external.find("/external"), overlay.find("/overlay"));
151 assert_eq!(external.find("/external"), deployment.find("snapshot"));
152 }
153}