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 = 256;
13
14//--------------------------------------------------------------------------------------------------
15// Types
16//--------------------------------------------------------------------------------------------------
17
18/// Progress events emitted during image pull and layer extraction.
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 extraction started.
62 LayerExtractStarted {
63 /// Layer index.
64 layer_index: usize,
65 /// Layer diff ID.
66 diff_id: Arc<str>,
67 },
68
69 /// Byte-level extraction progress for a single layer.
70 /// Tracks compressed bytes read from the layer tarball.
71 LayerExtractProgress {
72 /// Layer index (0-based).
73 layer_index: usize,
74 /// Compressed bytes read so far.
75 bytes_read: u64,
76 /// Total compressed file size.
77 total_bytes: u64,
78 },
79
80 /// Layer extraction completed.
81 LayerExtractComplete {
82 /// Layer index.
83 layer_index: usize,
84 /// Layer diff ID.
85 diff_id: Arc<str>,
86 },
87
88 /// Sidecar index generation started for a layer.
89 LayerIndexStarted {
90 /// Layer index.
91 layer_index: usize,
92 },
93
94 /// Sidecar index generation completed for a layer.
95 LayerIndexComplete {
96 /// Layer index.
97 layer_index: usize,
98 },
99
100 /// Entire image pull completed.
101 Complete {
102 /// The image reference.
103 reference: Arc<str>,
104 /// Number of layers.
105 layer_count: usize,
106 },
107}
108
109/// Receiver for progress events.
110pub struct PullProgressHandle {
111 rx: mpsc::Receiver<PullProgress>,
112}
113
114/// Emits progress events. Uses `try_send` — never blocks downloads.
115#[derive(Clone)]
116pub struct PullProgressSender {
117 tx: mpsc::Sender<PullProgress>,
118}
119
120//--------------------------------------------------------------------------------------------------
121// Methods
122//--------------------------------------------------------------------------------------------------
123
124impl PullProgressHandle {
125 /// Receive the next event. Returns `None` when the pull completes.
126 pub async fn recv(&mut self) -> Option<PullProgress> {
127 self.rx.recv().await
128 }
129
130 /// Convert into the underlying receiver for use with `tokio::select!`.
131 pub fn into_receiver(self) -> mpsc::Receiver<PullProgress> {
132 self.rx
133 }
134}
135
136impl PullProgressSender {
137 /// Emit a progress event. Silently discards if receiver is full or dropped.
138 pub fn send(&self, event: PullProgress) {
139 let _ = self.tx.try_send(event);
140 }
141}
142
143//--------------------------------------------------------------------------------------------------
144// Functions
145//--------------------------------------------------------------------------------------------------
146
147/// Create a progress channel pair.
148pub fn progress_channel() -> (PullProgressHandle, PullProgressSender) {
149 let (tx, rx) = mpsc::channel(DEFAULT_PROGRESS_CHANNEL_CAPACITY);
150 (PullProgressHandle { rx }, PullProgressSender { tx })
151}