prax_cli/
output.rs

1//! Styled terminal output utilities.
2
3use owo_colors::OwoColorize;
4
5/// Print a header/title
6pub fn header(text: &str) {
7    println!();
8    println!("{}", text.bold().cyan());
9    println!("{}", "─".repeat(text.len()).dimmed());
10    println!();
11}
12
13/// Print the Prax logo
14pub fn logo() {
15    let logo = r#"
16    ██████╗ ██████╗  █████╗ ██╗  ██╗
17    ██╔══██╗██╔══██╗██╔══██╗╚██╗██╔╝
18    ██████╔╝██████╔╝███████║ ╚███╔╝
19    ██╔═══╝ ██╔══██╗██╔══██║ ██╔██╗
20    ██║     ██║  ██║██║  ██║██╔╝ ██╗
21    ╚═╝     ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝
22    "#;
23    println!("{}", logo.bright_cyan().bold());
24}
25
26/// Print a section header
27pub fn section(text: &str) {
28    println!("{}", text.bold().white());
29}
30
31/// Print a key-value pair
32pub fn kv(key: &str, value: &str) {
33    println!("  {}: {}", key.dimmed(), value);
34}
35
36/// Print a success message
37pub fn success(text: &str) {
38    println!("{} {}", "✔".green().bold(), text.green());
39}
40
41/// Print an info message
42pub fn info(text: &str) {
43    println!("{} {}", "ℹ".blue().bold(), text);
44}
45
46/// Print a warning message
47pub fn warn(text: &str) {
48    println!("{} {}", "⚠".yellow().bold(), text.yellow());
49}
50
51/// Print an error message
52pub fn error(text: &str) {
53    eprintln!("{} {}", "✖".red().bold(), text.red());
54}
55
56/// Print a step indicator
57pub fn step(current: usize, total: usize, text: &str) {
58    println!("{} {}", format!("[{}/{}]", current, total).dimmed(), text);
59}
60
61/// Print a list header
62pub fn list(text: &str) {
63    println!("{}", text);
64}
65
66/// Print a list item
67pub fn list_item(text: &str) {
68    println!("  {} {}", "•".dimmed(), text);
69}
70
71/// Print a numbered list item
72pub fn numbered_item(number: usize, text: &str) {
73    println!("  {}. {}", number.to_string().dimmed(), text);
74}
75
76/// Print a newline
77pub fn newline() {
78    println!();
79}
80
81/// Print dimmed text
82pub fn dim(text: &str) {
83    println!("{}", text.dimmed());
84}
85
86/// Print code block with syntax highlighting hint
87pub fn code(code: &str, _language: &str) {
88    println!();
89    for line in code.lines() {
90        println!("  {}", line.bright_white());
91    }
92    println!();
93}
94
95/// Style text as success (green)
96pub fn style_success(text: &str) -> String {
97    text.green().to_string()
98}
99
100/// Style text as pending (yellow)
101pub fn style_pending(text: &str) -> String {
102    text.yellow().to_string()
103}
104
105/// Style text as error (red)
106pub fn style_error(text: &str) -> String {
107    text.red().to_string()
108}
109
110/// Ask for confirmation
111pub fn confirm(prompt: &str) -> bool {
112    use std::io::{self, Write};
113
114    print!("{} {} ", prompt, "[y/N]".dimmed());
115    io::stdout().flush().ok();
116
117    let mut input = String::new();
118    if io::stdin().read_line(&mut input).is_err() {
119        return false;
120    }
121
122    matches!(input.trim().to_lowercase().as_str(), "y" | "yes")
123}
124
125/// Ask for text input
126pub fn input(prompt: &str) -> Option<String> {
127    use std::io::{self, Write};
128
129    print!("{}: ", prompt);
130    io::stdout().flush().ok();
131
132    let mut input = String::new();
133    if io::stdin().read_line(&mut input).is_err() {
134        return None;
135    }
136
137    let trimmed = input.trim();
138    if trimmed.is_empty() {
139        None
140    } else {
141        Some(trimmed.to_string())
142    }
143}
144
145/// Ask user to select from options
146pub fn select(prompt: &str, options: &[&str]) -> Option<usize> {
147    use std::io::{self, Write};
148
149    println!("{}", prompt);
150    for (i, option) in options.iter().enumerate() {
151        println!("  {} {}", format!("{})", i + 1).dimmed(), option);
152    }
153
154    print!("{}: ", "Select".dimmed());
155    io::stdout().flush().ok();
156
157    let mut input = String::new();
158    if io::stdin().read_line(&mut input).is_err() {
159        return None;
160    }
161
162    input
163        .trim()
164        .parse::<usize>()
165        .ok()
166        .filter(|&n| n > 0 && n <= options.len())
167        .map(|n| n - 1)
168}