opendev_tui/widgets/
progress.rs1use ratatui::{
7 buffer::Buffer,
8 layout::Rect,
9 style::Style,
10 text::{Line, Span},
11 widgets::Widget,
12};
13
14use super::spinner::SpinnerState;
15use crate::formatters::style_tokens;
16
17#[derive(Debug, Clone)]
19pub struct TaskProgress {
20 pub description: String,
22 pub elapsed_secs: u64,
24 pub token_display: Option<String>,
26 pub interrupted: bool,
28 pub started_at: std::time::Instant,
30}
31
32pub struct TaskProgressWidget<'a> {
34 progress: &'a TaskProgress,
35 spinner_char: char,
36}
37
38impl<'a> TaskProgressWidget<'a> {
39 pub fn new(progress: &'a TaskProgress, spinner_state: &SpinnerState) -> Self {
40 Self {
41 progress,
42 spinner_char: spinner_state.current(),
43 }
44 }
45}
46
47impl Widget for TaskProgressWidget<'_> {
48 fn render(self, area: Rect, buf: &mut Buffer) {
49 if area.height == 0 || area.width == 0 {
50 return;
51 }
52
53 let mut spans: Vec<Span> = Vec::new();
54
55 spans.push(Span::styled(
57 format!("{} ", self.spinner_char),
58 Style::default().fg(style_tokens::BLUE_BRIGHT),
59 ));
60
61 spans.push(Span::styled(
63 format!("{}... ", self.progress.description),
64 Style::default().fg(style_tokens::SUBTLE),
65 ));
66
67 let mut info_parts = Vec::new();
69 info_parts.push("esc to interrupt".to_string());
70 info_parts.push(format!("{}s", self.progress.elapsed_secs));
71
72 if let Some(ref token_display) = self.progress.token_display {
73 info_parts.push(token_display.clone());
74 }
75
76 let info_str = info_parts.join(" \u{00b7} "); spans.push(Span::styled(
78 info_str,
79 Style::default().fg(style_tokens::SUBTLE),
80 ));
81
82 let line = Line::from(spans);
83 buf.set_line(area.left(), area.top(), &line, area.width);
84 }
85}
86
87pub fn format_final_status(progress: &TaskProgress) -> String {
91 let symbol = if progress.interrupted {
92 "\u{23f9}" } else {
94 "\u{23fa}" };
96
97 let status = if progress.interrupted {
98 "interrupted"
99 } else {
100 "completed"
101 };
102
103 let mut parts = vec![format!("{status} in {}s", progress.elapsed_secs)];
104 if let Some(ref token_display) = progress.token_display {
105 parts.push(token_display.clone());
106 }
107
108 format!("{symbol} {}", parts.join(", "))
109}
110
111#[cfg(test)]
112#[path = "progress_tests.rs"]
113mod tests;