ghostscope_ui/components/loading/
state.rs1use std::time::Instant;
2
3#[derive(Debug, Clone, PartialEq)]
5pub enum LoadingState {
6 Initializing,
8 ConnectingToRuntime,
10 LoadingSymbols { progress: Option<f64> },
12 LoadingSourceCode,
14 Ready,
16 Failed(String),
18}
19
20impl LoadingState {
21 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 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 pub fn is_ready(&self) -> bool {
44 matches!(self, LoadingState::Ready)
45 }
46
47 pub fn is_failed(&self) -> bool {
49 matches!(self, LoadingState::Failed(_))
50 }
51}
52
53#[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>, }
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#[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}