Skip to main content

ghostscope_ui/components/loading/
state.rs

1use std::time::Instant;
2
3/// Loading states for different initialization phases
4#[derive(Debug, Clone, PartialEq)]
5pub enum LoadingState {
6    /// Application is starting up
7    Initializing,
8    /// Waiting for runtime to connect
9    ConnectingToRuntime,
10    /// Waiting for DWARF symbols to load
11    LoadingSymbols { progress: Option<f64> },
12    /// Waiting for source code information
13    LoadingSourceCode,
14    /// Loading completed, application ready
15    Ready,
16    /// Loading failed with error
17    Failed(String),
18}
19
20impl LoadingState {
21    /// Get display message for current loading state
22    pub fn message(&self) -> &str {
23        match self {
24            LoadingState::Initializing => "Initializing application...",
25            LoadingState::ConnectingToRuntime => "Connecting to runtime...",
26            LoadingState::LoadingSymbols { .. } => "Loading debug information...",
27            LoadingState::LoadingSourceCode => "Loading source code information...",
28            LoadingState::Ready => "Ready",
29            LoadingState::Failed(error) => error,
30        }
31    }
32
33    /// Get progress value (0.0 to 1.0) if available
34    pub fn progress(&self) -> Option<f64> {
35        match self {
36            LoadingState::LoadingSymbols { progress } => *progress,
37            LoadingState::Ready => Some(1.0),
38            _ => None,
39        }
40    }
41
42    /// Check if loading is complete
43    pub fn is_ready(&self) -> bool {
44        matches!(self, LoadingState::Ready)
45    }
46
47    /// Check if loading failed
48    pub fn is_failed(&self) -> bool {
49        matches!(self, LoadingState::Failed(_))
50    }
51}
52
53/// Module loading status for individual modules
54#[derive(Debug, Clone)]
55pub struct ModuleLoadStatus {
56    pub path: String,
57    pub state: ModuleState,
58    pub stats: Option<ModuleStats>,
59    pub start_time: Option<Instant>,
60    pub load_time: Option<f64>, // seconds
61}
62
63#[derive(Debug, Clone)]
64pub enum ModuleState {
65    Queued,
66    Loading,
67    Completed,
68    Failed(String),
69}
70
71#[derive(Debug, Clone)]
72pub struct ModuleStats {
73    pub functions: usize,
74    pub variables: usize,
75    pub types: usize,
76    pub debug_source: String,
77    pub debug_source_path: Option<String>,
78}
79
80#[derive(Debug, Clone, Default)]
81pub struct DebugSourceCounts {
82    pub embedded: usize,
83    pub explicit: usize,
84    pub debuglink: usize,
85    pub debuginfod: usize,
86    pub missing: usize,
87    pub other: usize,
88}
89
90impl DebugSourceCounts {
91    fn record(&mut self, source: &str) {
92        match source {
93            "embedded" => self.embedded += 1,
94            "explicit" => self.explicit += 1,
95            "debuglink" => self.debuglink += 1,
96            "debuginfod" => self.debuginfod += 1,
97            "missing" => self.missing += 1,
98            _ => self.other += 1,
99        }
100    }
101
102    pub fn summary(&self) -> String {
103        let mut parts = Vec::new();
104        push_nonzero(&mut parts, "embedded", self.embedded);
105        push_nonzero(&mut parts, "explicit", self.explicit);
106        push_nonzero(&mut parts, "debuglink", self.debuglink);
107        push_nonzero(&mut parts, "debuginfod", self.debuginfod);
108        push_nonzero(&mut parts, "missing", self.missing);
109        push_nonzero(&mut parts, "other", self.other);
110        parts.join("  ")
111    }
112
113    pub fn has_counts(&self) -> bool {
114        self.embedded + self.explicit + self.debuglink + self.debuginfod + self.missing + self.other
115            > 0
116    }
117}
118
119fn push_nonzero(parts: &mut Vec<String>, label: &str, count: usize) {
120    if count > 0 {
121        parts.push(format!("{label} {count}"));
122    }
123}
124
125impl ModuleLoadStatus {
126    pub fn new(path: String) -> Self {
127        Self {
128            path,
129            state: ModuleState::Queued,
130            stats: None,
131            start_time: None,
132            load_time: None,
133        }
134    }
135
136    pub fn start_loading(&mut self) {
137        self.state = ModuleState::Loading;
138        self.start_time = Some(Instant::now());
139    }
140
141    pub fn complete(&mut self, stats: ModuleStats) {
142        if let Some(start_time) = self.start_time {
143            self.load_time = Some(start_time.elapsed().as_secs_f64());
144        }
145        self.state = ModuleState::Completed;
146        self.stats = Some(stats);
147    }
148
149    pub fn fail(&mut self, error: String) {
150        if let Some(start_time) = self.start_time {
151            self.load_time = Some(start_time.elapsed().as_secs_f64());
152        }
153        self.state = ModuleState::Failed(error);
154    }
155}
156
157/// Overall loading progress tracking
158#[derive(Debug, Clone)]
159pub struct LoadingProgress {
160    pub start_time: Instant,
161    pub modules: Vec<ModuleLoadStatus>,
162    pub completed_count: usize,
163    pub failed_count: usize,
164    pub current_loading: Option<String>,
165    pub debug_sources: DebugSourceCounts,
166}
167
168impl LoadingProgress {
169    pub fn new() -> Self {
170        Self {
171            start_time: Instant::now(),
172            modules: Vec::new(),
173            completed_count: 0,
174            failed_count: 0,
175            current_loading: None,
176            debug_sources: DebugSourceCounts::default(),
177        }
178    }
179
180    pub fn add_module(&mut self, path: String) {
181        self.modules.push(ModuleLoadStatus::new(path));
182    }
183
184    pub fn start_module_loading(&mut self, path: &str) {
185        if let Some(module) = self.modules.iter_mut().find(|m| m.path == path) {
186            module.start_loading();
187            self.current_loading = Some(path.to_string());
188        }
189    }
190
191    pub fn complete_module(&mut self, path: &str, stats: ModuleStats) {
192        if let Some(module) = self.modules.iter_mut().find(|m| m.path == path) {
193            self.debug_sources.record(&stats.debug_source);
194            module.complete(stats);
195            self.completed_count += 1;
196            if self.current_loading.as_deref() == Some(path) {
197                self.current_loading = None;
198            }
199        }
200    }
201
202    pub fn fail_module(&mut self, path: &str, error: String) {
203        if let Some(module) = self.modules.iter_mut().find(|m| m.path == path) {
204            module.fail(error);
205            self.failed_count += 1;
206            if self.current_loading.as_deref() == Some(path) {
207                self.current_loading = None;
208            }
209        }
210    }
211
212    pub fn total_modules(&self) -> usize {
213        self.modules.len()
214    }
215
216    pub fn progress_ratio(&self) -> f64 {
217        if self.modules.is_empty() {
218            0.0
219        } else {
220            (self.completed_count + self.failed_count) as f64 / self.modules.len() as f64
221        }
222    }
223
224    pub fn elapsed_time(&self) -> f64 {
225        self.start_time.elapsed().as_secs_f64()
226    }
227
228    pub fn recently_completed(&self, limit: usize) -> Vec<&ModuleLoadStatus> {
229        self.modules
230            .iter()
231            .filter(|m| matches!(m.state, ModuleState::Completed))
232            .rev()
233            .take(limit)
234            .collect()
235    }
236
237    pub fn recently_failed(&self, limit: usize) -> Vec<&ModuleLoadStatus> {
238        self.modules
239            .iter()
240            .filter(|m| matches!(m.state, ModuleState::Failed(_)))
241            .rev()
242            .take(limit)
243            .collect()
244    }
245
246    pub fn recently_finished(&self, limit: usize) -> Vec<&ModuleLoadStatus> {
247        self.modules
248            .iter()
249            .filter(|m| matches!(m.state, ModuleState::Completed | ModuleState::Failed(_)))
250            .rev()
251            .take(limit)
252            .collect()
253    }
254
255    pub fn total_stats(&self) -> ModuleStats {
256        let mut total = ModuleStats {
257            functions: 0,
258            variables: 0,
259            types: 0,
260            debug_source: "summary".to_string(),
261            debug_source_path: None,
262        };
263
264        for module in &self.modules {
265            if let Some(stats) = &module.stats {
266                total.functions += stats.functions;
267                total.variables += stats.variables;
268                total.types += stats.types;
269            }
270        }
271
272        total
273    }
274
275    pub fn is_complete(&self) -> bool {
276        !self.modules.is_empty() && (self.completed_count + self.failed_count) == self.modules.len()
277    }
278}
279
280impl Default for LoadingProgress {
281    fn default() -> Self {
282        Self::new()
283    }
284}