cascade_cli/cli/
output.rs1use console::{style, Color, Emoji, Style};
2use std::fmt::Display;
3
4struct Theme;
7
8impl Theme {
9 fn success_style() -> Style {
12 Style::new().color256(46).bold() }
14
15 const ERROR: Color = Color::Red;
17
18 const WARNING: Color = Color::Yellow;
20
21 fn info_style() -> Style {
24 Style::new().color256(35) }
26
27 fn tip_style() -> Style {
29 Style::new().color256(35) }
31
32 fn dim_style() -> Style {
34 Style::new().dim()
35 }
36}
37
38pub struct Output;
40
41impl Output {
42 pub fn success<T: Display>(message: T) {
44 println!("{} {}", Theme::success_style().apply_to("✓"), message);
45 }
46
47 pub fn error<T: Display>(message: T) {
49 println!("{} {}", style("✗").fg(Theme::ERROR), message);
50 }
51
52 pub fn warning<T: Display>(message: T) {
54 println!("{} {}", style("⚠").fg(Theme::WARNING), message);
55 }
56
57 pub fn info<T: Display>(message: T) {
59 println!("{} {}", Theme::info_style().apply_to("ℹ"), message);
60 }
61
62 pub fn sub_item<T: Display>(message: T) {
64 println!(" {} {}", Theme::dim_style().apply_to("→"), message);
65 }
66
67 pub fn bullet<T: Display>(message: T) {
69 println!(" {} {}", Theme::dim_style().apply_to("•"), message);
70 }
71
72 pub fn section<T: Display>(title: T) {
74 println!("\n{}", style(title).bold().underlined());
75 }
76
77 pub fn tip<T: Display>(message: T) {
79 println!(
80 "{} {}",
81 Theme::tip_style().apply_to("TIP:"),
82 Theme::dim_style().apply_to(message)
83 );
84 }
85
86 pub fn progress<T: Display>(message: T) {
88 print!("{} {}", Theme::info_style().apply_to("→"), message);
89 use std::io::{self, Write};
90 io::stdout().flush().unwrap();
91 }
92
93 pub fn success_inline() {
95 println!(" {}", Theme::success_style().apply_to("✓"));
96 }
97
98 pub fn error_inline<T: Display>(message: T) {
100 if message.to_string().is_empty() {
101 println!(" {}", style("✗").fg(Theme::ERROR));
102 } else {
103 println!(" {} {}", style("✗").fg(Theme::ERROR), message);
104 }
105 }
106
107 pub fn divider() {
109 println!("{}", Theme::dim_style().apply_to("─".repeat(50)));
110 }
111
112 pub fn stack_info(
114 name: &str,
115 id: &str,
116 base_branch: &str,
117 working_branch: Option<&str>,
118 is_active: bool,
119 ) {
120 println!(
122 "{} {}",
123 Theme::info_style().apply_to("Stack:"),
124 style(name).bold()
125 );
126 Self::sub_item(format!("Stack ID: {}", Theme::dim_style().apply_to(id)));
127 Self::sub_item(format!(
128 "Base branch: {}",
129 Theme::info_style().apply_to(base_branch)
130 ));
131
132 if let Some(working) = working_branch {
133 Self::sub_item(format!(
134 "Working branch: {}",
135 Theme::info_style().apply_to(working)
136 ));
137 }
138
139 if is_active {
140 Self::sub_item(format!(
141 "Status: {}",
142 Theme::success_style().apply_to("Active")
143 ));
144 }
145 }
146
147 pub fn next_steps(steps: &[&str]) {
149 println!();
150 Self::tip("Next steps:");
151 for step in steps {
152 Self::bullet(step);
153 }
154 }
155
156 pub fn command_example<T: Display>(command: T) {
158 println!(" {}", style(command).fg(Theme::WARNING));
159 }
160
161 pub fn check_start<T: Display>(message: T) {
163 println!("\n{} {}", style("🔍").bright(), style(message).bold());
164 }
165
166 pub fn solution<T: Display>(message: T) {
168 println!(" {}: {}", style("Solution").fg(Theme::WARNING), message);
169 }
170
171 pub fn numbered_item<T: Display>(number: usize, message: T) {
173 println!(" {}. {}", Theme::info_style().apply_to(number), message);
174 }
175
176 pub fn spacing() {
178 println!();
179 }
180
181 pub fn entry_status(is_submitted: bool, is_merged: bool) -> String {
186 if is_merged {
187 format!("{}", Theme::success_style().apply_to("[merged]"))
188 } else if is_submitted {
189 format!("{}", Theme::info_style().apply_to("[submitted]"))
190 } else {
191 format!("{}", style("[pending]").fg(Theme::WARNING))
192 }
193 }
194}
195
196pub struct Emojis;
198
199impl Emojis {
200 pub const SUCCESS: Emoji<'_, '_> = Emoji("✓", "OK");
201 pub const ERROR: Emoji<'_, '_> = Emoji("✗", "ERROR");
202 pub const WARNING: Emoji<'_, '_> = Emoji("⚠", "WARNING");
203 pub const INFO: Emoji<'_, '_> = Emoji("ℹ", "INFO");
204 pub const TIP: Emoji<'_, '_> = Emoji("💡", "TIP");
205 pub const ROCKET: Emoji<'_, '_> = Emoji("🚀", "ROCKET");
206 pub const SEARCH: Emoji<'_, '_> = Emoji("🔍", "SEARCH");
207 pub const UPLOAD: Emoji<'_, '_> = Emoji("📤", "UPLOAD");
208 pub const STACK: Emoji<'_, '_> = Emoji("📊", "STACK");
209}