1use std::sync::atomic::{AtomicI32, AtomicU64, Ordering};
7use std::sync::{Arc, Mutex};
8
9#[derive(Debug, Clone)]
11pub enum ProgressType {
12 Spinner,
14 Bar { total: u64 },
16}
17
18#[derive(Debug, Clone)]
20pub enum StubStage {
21 Start,
22 SignatureKey,
23 RamStub,
24}
25
26#[derive(Debug, Clone)]
28pub enum EraseFlashStyle {
29 Complete,
30 Addressed,
31}
32
33#[derive(Debug, Clone)]
35pub enum EraseRegionStyle {
36 LegacyFlashStartDecimalLength,
37 HexLength,
38 Range,
39}
40
41#[derive(Debug, Clone)]
43pub enum ProgressOperation {
44 Connect,
45 DownloadStub {
46 stage: StubStage,
47 },
48 EraseFlash {
49 address: u32,
50 style: EraseFlashStyle,
51 },
52 EraseRegion {
53 address: u32,
54 len: u32,
55 style: EraseRegionStyle,
56 },
57 EraseAllRegions,
58 Verify {
59 address: u32,
60 len: u32,
61 },
62 CheckRedownload {
63 address: u32,
64 size: u64,
65 },
66 WriteFlash {
67 address: u32,
68 size: u64,
69 },
70 ReadFlash {
71 address: u32,
72 size: u32,
73 },
74}
75
76#[derive(Debug, Clone)]
78pub struct ProgressContext {
79 pub step: i32,
81 pub progress_type: ProgressType,
83 pub operation: ProgressOperation,
85 pub current: Option<u64>,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub struct ProgressId(pub u64);
92
93#[derive(Debug, Clone)]
95pub enum ProgressStatus {
96 Success,
97 Retry,
98 Skipped,
99 Required,
100 NotFound,
101 Failed(String),
102 Aborted,
103}
104
105#[derive(Debug, Clone)]
107pub enum ProgressEvent {
108 Start {
109 id: ProgressId,
110 ctx: ProgressContext,
111 },
112 Update {
113 id: ProgressId,
114 ctx: ProgressContext,
115 },
116 Advance {
117 id: ProgressId,
118 delta: u64,
119 },
120 Finish {
121 id: ProgressId,
122 status: ProgressStatus,
123 },
124}
125
126pub trait ProgressSink: Send + Sync {
128 fn on_event(&self, event: ProgressEvent);
129}
130
131pub type ProgressSinkArc = Arc<dyn ProgressSink>;
133
134#[derive(Debug, Default)]
136pub struct NoOpProgressSink;
137
138impl ProgressSink for NoOpProgressSink {
139 fn on_event(&self, _event: ProgressEvent) {}
140}
141
142pub fn no_op_progress_sink() -> ProgressSinkArc {
144 Arc::new(NoOpProgressSink)
145}
146
147pub struct ProgressHelper {
149 sink: ProgressSinkArc,
150 step_counter: Arc<AtomicI32>,
151 id_counter: Arc<AtomicU64>,
152}
153
154impl ProgressHelper {
155 pub fn new(sink: ProgressSinkArc, initial_step: i32) -> Self {
157 Self {
158 sink,
159 step_counter: Arc::new(AtomicI32::new(initial_step)),
160 id_counter: Arc::new(AtomicU64::new(1)),
161 }
162 }
163
164 fn next_step(&self) -> i32 {
166 self.step_counter.fetch_add(1, Ordering::SeqCst)
167 }
168
169 fn next_id(&self) -> ProgressId {
171 ProgressId(self.id_counter.fetch_add(1, Ordering::SeqCst))
172 }
173
174 pub fn create_spinner(&self, operation: ProgressOperation) -> ProgressHandle {
176 let step = self.next_step();
177 let id = self.next_id();
178 let ctx = ProgressContext {
179 step,
180 progress_type: ProgressType::Spinner,
181 operation,
182 current: None,
183 };
184 self.sink.on_event(ProgressEvent::Start {
185 id,
186 ctx: ctx.clone(),
187 });
188 ProgressHandle::new(Arc::clone(&self.sink), id, ctx)
189 }
190
191 pub fn create_bar(&self, total: u64, operation: ProgressOperation) -> ProgressHandle {
193 let step = self.next_step();
194 let id = self.next_id();
195 let ctx = ProgressContext {
196 step,
197 progress_type: ProgressType::Bar { total },
198 operation,
199 current: Some(0),
200 };
201 self.sink.on_event(ProgressEvent::Start {
202 id,
203 ctx: ctx.clone(),
204 });
205 ProgressHandle::new(Arc::clone(&self.sink), id, ctx)
206 }
207
208 pub fn current_step(&self) -> i32 {
210 self.step_counter.load(Ordering::SeqCst)
211 }
212
213 pub fn sync_step_to_external(&self, external_step: &mut i32) {
215 *external_step = self.current_step();
216 }
217}
218
219pub struct ProgressHandle {
221 sink: ProgressSinkArc,
222 id: ProgressId,
223 context: Mutex<ProgressContext>,
224 finished: bool,
225}
226
227impl ProgressHandle {
228 fn new(sink: ProgressSinkArc, id: ProgressId, context: ProgressContext) -> Self {
229 Self {
230 sink,
231 id,
232 context: Mutex::new(context),
233 finished: false,
234 }
235 }
236
237 pub fn set_operation(&self, operation: ProgressOperation) {
239 let mut ctx = self.context.lock().unwrap();
240 ctx.operation = operation;
241 self.sink.on_event(ProgressEvent::Update {
242 id: self.id,
243 ctx: ctx.clone(),
244 });
245 }
246
247 pub fn inc(&self, delta: u64) {
249 self.sink
250 .on_event(ProgressEvent::Advance { id: self.id, delta });
251 }
252
253 pub fn finish(mut self, status: ProgressStatus) {
255 self.finished = true;
256 self.sink.on_event(ProgressEvent::Finish {
257 id: self.id,
258 status,
259 });
260 }
261}
262
263impl Drop for ProgressHandle {
264 fn drop(&mut self) {
265 if !self.finished {
266 self.sink.on_event(ProgressEvent::Finish {
267 id: self.id,
268 status: ProgressStatus::Aborted,
269 });
270 }
271 }
272}