1use crate::style::Color;
6use std::io::Write;
7
8pub mod text {
10 pub fn truncate(text: &str, max_length: usize) -> String {
19 text.chars().take(max_length).collect()
20 }
21
22 pub fn pad(text: &str, length: usize, fill_char: char) -> String {
32 if text.len() >= length {
33 text.chars().take(length).collect()
34 }
35 else {
36 format!("{}{}", text, fill_char.to_string().repeat(length - text.len()))
37 }
38 }
39
40 pub fn center(text: &str, width: usize) -> String {
49 if text.len() >= width {
50 text.chars().take(width).collect()
51 }
52 else {
53 let padding = (width - text.len()) / 2;
54 format!("{}{}{}", " ".repeat(padding), text, " ".repeat(width - text.len() - padding))
55 }
56 }
57}
58
59pub mod color {
61 use super::Color;
62 use crossterm::style as crossterm_style;
63
64 pub fn rgb(r: u8, g: u8, b: u8) -> Color {
74 Color::Rgb(r, g, b)
75 }
76
77 pub fn to_crossterm_color(color: Color) -> crossterm_style::Color {
85 color.into()
86 }
87}
88
89pub mod terminal_utils {
91 use crossterm::{cursor, terminal as crossterm_terminal};
92 use std::io::{self, Result as CrosstermResult, Write};
93
94 #[allow(dead_code)]
99 pub fn clear() -> CrosstermResult<()> {
100 let mut stdout = io::stdout();
101 write!(stdout, "{}", crossterm_terminal::Clear(crossterm_terminal::ClearType::All))?;
102 stdout.flush()?;
103 Ok(())
104 }
105
106 #[allow(dead_code)]
115 pub fn move_cursor(x: u16, y: u16) -> CrosstermResult<()> {
116 let mut stdout = io::stdout();
117 write!(stdout, "{}", cursor::MoveTo(x, y))?;
118 stdout.flush()?;
119 Ok(())
120 }
121
122 #[allow(dead_code)]
127 pub fn hide_cursor() -> CrosstermResult<()> {
128 let mut stdout = io::stdout();
129 write!(stdout, "{}", cursor::Hide)?;
130 stdout.flush()?;
131 Ok(())
132 }
133
134 #[allow(dead_code)]
139 pub fn show_cursor() -> CrosstermResult<()> {
140 let mut stdout = io::stdout();
141 write!(stdout, "{}", cursor::Show)?;
142 stdout.flush()?;
143 Ok(())
144 }
145}
146
147pub mod math {
149 pub fn clamp<T: PartialOrd>(value: T, min: T, max: T) -> T {
159 if value < min {
160 min
161 }
162 else if value > max {
163 max
164 }
165 else {
166 value
167 }
168 }
169
170 pub fn lerp(start: f64, end: f64, t: f64) -> f64 {
180 start + (end - start) * t
181 }
182}
183
184pub fn select<T>(items: &[T], prompt: &str) -> Option<usize>
186where
187 T: std::fmt::Display,
188{
189 println!("{}", prompt);
190 for (i, item) in items.iter().enumerate() {
191 println!("{}: {}", i + 1, item);
192 }
193
194 loop {
195 print!("请选择 (1-{}): ", items.len());
196 std::io::stdout().flush().unwrap();
197
198 let mut input = String::new();
199 std::io::stdin().read_line(&mut input).unwrap();
200
201 match input.trim().parse::<usize>() {
202 Ok(choice) if choice >= 1 && choice <= items.len() => {
203 return Some(choice - 1);
204 }
205 _ => {
206 println!("无效的选择,请重新输入");
207 }
208 }
209 }
210}
211
212pub fn loading_animation(duration: std::time::Duration, message: &str) {
214 let chars = ["|", "/", "-", "\\"];
215 let start = std::time::Instant::now();
216
217 print!("{}", message);
218 std::io::stdout().flush().unwrap();
219
220 let mut i = 0;
221 while start.elapsed() < duration {
222 print!("\r{}{}", message, chars[i % chars.len()]);
223 std::io::stdout().flush().unwrap();
224 std::thread::sleep(std::time::Duration::from_millis(100));
225 i += 1;
226 }
227
228 print!("\r{}{}", message, " ");
229 println!();
230}