microsandbox_image/progress.rs
1//! Pull progress reporting.
2
3use std::sync::Arc;
4
5use tokio::sync::mpsc;
6
7//--------------------------------------------------------------------------------------------------
8// Constants
9//--------------------------------------------------------------------------------------------------
10
11/// Default channel capacity.
12const DEFAULT_PROGRESS_CHANNEL_CAPACITY: usize = 1024;
13
14//--------------------------------------------------------------------------------------------------
15// Types
16//--------------------------------------------------------------------------------------------------
17
18/// Progress events emitted during image pull and EROFS materialization.
19#[derive(Debug, Clone)]
20pub enum PullProgress {
21 /// Resolving the image reference.
22 Resolving {
23 /// The image reference being resolved.
24 reference: Arc<str>,
25 },
26
27 /// Manifest parsed. Layer count and total sizes now known.
28 Resolved {
29 /// The image reference.
30 reference: Arc<str>,
31 /// Resolved manifest digest.
32 manifest_digest: Arc<str>,
33 /// Number of layers.
34 layer_count: usize,
35 /// Sum of compressed layer sizes. `None` if manifest omits sizes.
36 total_download_bytes: Option<u64>,
37 },
38
39 /// Byte-level download progress for a single layer.
40 LayerDownloadProgress {
41 /// Layer index (0-based).
42 layer_index: usize,
43 /// Layer digest.
44 digest: Arc<str>,
45 /// Bytes downloaded so far.
46 downloaded_bytes: u64,
47 /// Total bytes (if known).
48 total_bytes: Option<u64>,
49 },
50
51 /// A single layer download completed and verified.
52 LayerDownloadComplete {
53 /// Layer index.
54 layer_index: usize,
55 /// Layer digest.
56 digest: Arc<str>,
57 /// Total downloaded bytes.
58 downloaded_bytes: u64,
59 },
60
61 /// Layer download completed and the blob is being verified.
62 LayerDownloadVerifying {
63 /// Layer index.
64 layer_index: usize,
65 /// Layer digest.
66 digest: Arc<str>,
67 },
68
69 /// Layer EROFS materialization started.
70 LayerMaterializeStarted {
71 /// Layer index.
72 layer_index: usize,
73 /// Layer diff ID.
74 diff_id: Arc<str>,
75 },
76
77 /// Byte-level materialization progress for a single layer.
78 LayerMaterializeProgress {
79 /// Layer index (0-based).
80 layer_index: usize,
81 /// Bytes read so far.
82 bytes_read: u64,
83 /// Total bytes.
84 total_bytes: u64,
85 },
86
87 /// Layer tar ingest is complete and the EROFS image is being written.
88 LayerMaterializeWriting {
89 /// Layer index.
90 layer_index: usize,
91 },
92
93 /// Layer EROFS materialization completed.
94 LayerMaterializeComplete {
95 /// Layer index.
96 layer_index: usize,
97 /// Layer diff ID.
98 diff_id: Arc<str>,
99 },
100
101 /// Merging per-layer trees into the unified rootfs view.
102 StitchMergingTrees {
103 /// Number of layers being merged.
104 layer_count: usize,
105 },
106
107 /// Writing the fsmeta EROFS image (metadata-only merged view).
108 StitchWritingFsmeta,
109
110 /// Writing the VMDK descriptor that stitches fsmeta + layer EROFSes.
111 StitchWritingVmdk,
112
113 /// Stitching phase finished — fsmeta + VMDK are on disk.
114 StitchComplete,
115
116 /// Entire image pull completed.
117 Complete {
118 /// The image reference.
119 reference: Arc<str>,
120 /// Number of layers.
121 layer_count: usize,
122 },
123}
124
125/// Receiver for progress events.
126pub struct PullProgressHandle {
127 rx: mpsc::Receiver<PullProgress>,
128}
129
130/// Emits progress events. Uses `try_send` — never blocks downloads.
131#[derive(Clone)]
132pub struct PullProgressSender {
133 tx: mpsc::Sender<PullProgress>,
134}
135
136//--------------------------------------------------------------------------------------------------
137// Methods
138//--------------------------------------------------------------------------------------------------
139
140impl PullProgressHandle {
141 /// Receive the next event. Returns `None` when the pull completes.
142 pub async fn recv(&mut self) -> Option<PullProgress> {
143 self.rx.recv().await
144 }
145
146 /// Convert into the underlying receiver for use with `tokio::select!`.
147 pub fn into_receiver(self) -> mpsc::Receiver<PullProgress> {
148 self.rx
149 }
150}
151
152impl PullProgressSender {
153 /// Emit a progress event. Silently discards if receiver is full or dropped.
154 pub fn send(&self, event: PullProgress) {
155 let _ = self.tx.try_send(event);
156 }
157}
158
159//--------------------------------------------------------------------------------------------------
160// Functions
161//--------------------------------------------------------------------------------------------------
162
163/// Create a progress channel pair.
164pub fn progress_channel() -> (PullProgressHandle, PullProgressSender) {
165 let (tx, rx) = mpsc::channel(DEFAULT_PROGRESS_CHANNEL_CAPACITY);
166 (PullProgressHandle { rx }, PullProgressSender { tx })
167}