Skip to main content

dm_database_sqllog2db/tui/
app.rs

1#[cfg(feature = "tui")]
2use super::progress::ProgressTracker;
3#[cfg(feature = "tui")]
4use std::time::Instant;
5
6/// TUI 应用状态
7#[cfg(feature = "tui")]
8#[derive(Debug, Clone)]
9pub struct TuiApp {
10    /// 当前处理文件索引
11    pub current_file_index: usize,
12    /// 总文件数
13    pub total_files: usize,
14    /// 当前文件名
15    pub current_file_name: String,
16    /// 已导出记录数
17    pub exported_records: usize,
18    /// 错误记录数
19    pub error_records: usize,
20    /// 任务开始时间
21    pub start_time: Option<Instant>,
22    /// 是否完成
23    pub is_finished: bool,
24    /// 导出器名称
25    pub exporter_name: String,
26    /// 进度跟踪器(可选,用于同步共享状态)
27    #[cfg_attr(feature = "tui", allow(dead_code))]
28    progress_tracker: Option<ProgressTracker>,
29}
30
31#[cfg(feature = "tui")]
32impl TuiApp {
33    #[must_use]
34    pub fn new(total_files: usize, exporter_name: String) -> Self {
35        Self {
36            current_file_index: 0,
37            total_files,
38            current_file_name: String::new(),
39            exported_records: 0,
40            error_records: 0,
41            start_time: None,
42            is_finished: false,
43            exporter_name,
44            progress_tracker: None,
45        }
46    }
47
48    #[must_use]
49    pub fn with_progress_tracker(mut self, tracker: ProgressTracker) -> Self {
50        self.progress_tracker = Some(tracker);
51        self
52    }
53
54    pub fn start(&mut self) {
55        self.start_time = Some(Instant::now());
56    }
57
58    pub fn set_file(&mut self, index: usize, name: String) {
59        self.current_file_index = index;
60        self.current_file_name = name;
61    }
62
63    pub fn add_records(&mut self, count: usize) {
64        self.exported_records += count;
65    }
66
67    pub fn add_errors(&mut self, count: usize) {
68        self.error_records += count;
69    }
70
71    pub fn finish(&mut self) {
72        self.is_finished = true;
73    }
74
75    #[must_use]
76    pub fn progress_percent(&self) -> u16 {
77        if self.total_files == 0 {
78            return 0;
79        }
80
81        let scaled = self.current_file_index.saturating_mul(100) / self.total_files;
82        u16::try_from(scaled.min(100)).unwrap_or(100)
83    }
84
85    #[must_use]
86    pub fn elapsed_secs(&self) -> f64 {
87        self.start_time.map_or(0.0, |t| t.elapsed().as_secs_f64())
88    }
89
90    #[must_use]
91    pub fn throughput(&self) -> u64 {
92        let elapsed_ms = self.start_time.map_or(0, |t| t.elapsed().as_millis());
93
94        if elapsed_ms > 0 && self.exported_records > 0 {
95            let per_sec = (self.exported_records as u128 * 1_000) / elapsed_ms;
96            u64::try_from(per_sec).unwrap_or(u64::MAX)
97        } else {
98            0
99        }
100    }
101}