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