1use crate::constants::time_function;
2use crate::{error, info, usage};
3use indicatif::{ProgressBar, ProgressStyle};
4use std::io::{self, Write};
5
6pub fn handle_time(function: &str, arg: &str) {
9 if function != time_function::COUNTDOWN {
10 error!("❌ 未知的功能: {},目前仅支持 countdown", function);
11 usage!("j time countdown <duration>");
12 info!(
13 " duration 格式: 30s(秒), 5m(分钟), 1h(小时), 不带单位默认为分钟"
14 );
15 return;
16 }
17
18 let duration_secs = parse_duration(arg);
19 if duration_secs <= 0 {
20 error!("❌ 无效的时长: {}", arg);
21 return;
22 }
23
24 info!("⏳ 倒计时开始:{}", format_duration_display(duration_secs as u64));
25 run_countdown(duration_secs as u64);
26}
27
28fn format_duration_display(secs: u64) -> String {
30 if secs >= 3600 {
31 let h = secs / 3600;
32 let m = (secs % 3600) / 60;
33 if m > 0 {
34 format!("{}小时{}分钟", h, m)
35 } else {
36 format!("{}小时", h)
37 }
38 } else if secs >= 60 {
39 let m = secs / 60;
40 let s = secs % 60;
41 if s > 0 {
42 format!("{}分{}秒", m, s)
43 } else {
44 format!("{}分钟", m)
45 }
46 } else {
47 format!("{}秒", secs)
48 }
49}
50
51fn parse_duration(s: &str) -> i64 {
53 let s = s.trim();
54 if s.ends_with('s') {
55 s[..s.len() - 1].parse::<i64>().unwrap_or(-1)
56 } else if s.ends_with('m') {
57 s[..s.len() - 1]
58 .parse::<i64>()
59 .map(|m| m * 60)
60 .unwrap_or(-1)
61 } else if s.ends_with('h') {
62 s[..s.len() - 1]
63 .parse::<i64>()
64 .map(|h| h * 3600)
65 .unwrap_or(-1)
66 } else {
67 s.parse::<i64>().map(|m| m * 60).unwrap_or(-1)
69 }
70}
71
72fn format_remaining(secs: u64) -> String {
74 if secs >= 3600 {
75 format!("{:02}:{:02}:{:02}", secs / 3600, (secs % 3600) / 60, secs % 60)
76 } else {
77 format!("{:02}:{:02}", secs / 60, secs % 60)
78 }
79}
80
81fn run_countdown(total_secs: u64) {
83 let pb = ProgressBar::new(total_secs);
84
85 pb.set_style(
87 ProgressStyle::default_bar()
88 .template(" {spinner:.cyan} ⏱️ {msg} {wide_bar:.cyan/dark_gray} {percent}%")
89 .unwrap()
90 .progress_chars("━╸─")
91 );
92
93 pb.set_message(format_remaining(total_secs));
94
95 let start = std::time::Instant::now();
96
97 for elapsed in 1..=total_secs {
98 let next_tick = start + std::time::Duration::from_secs(elapsed);
100 let now = std::time::Instant::now();
101 if next_tick > now {
102 std::thread::sleep(next_tick - now);
103 }
104
105 let remaining = total_secs - elapsed;
106 pb.set_position(elapsed);
107 pb.set_message(format_remaining(remaining));
108 }
109
110 pb.finish_and_clear();
111
112 println!(" 🎉 Time's up! 倒计时结束!");
113 println!();
114
115 display_celebration();
117}
118
119fn display_celebration() {
121 let frames = [
122 " 🔔 Ding Ding! Time's Up! 🔔",
123 " 💢😤💢 Stop! Stop! Stop! 💢😤💢",
124 " 🔥😠🔥 How dare you don't stop! 🔥😠🔥",
125 ];
126
127 #[cfg(target_os = "macos")]
129 {
130 let _ = std::process::Command::new("afplay")
131 .arg("/System/Library/Sounds/Glass.aiff")
132 .spawn();
133 }
134
135 for i in 0..6 {
136 print!("\r{:<60}", frames[i % frames.len()]);
138 let _ = io::stdout().flush();
139 std::thread::sleep(std::time::Duration::from_millis(600));
140 }
141 println!();
142}