ic_testkit/artifacts/
wasm_batch.rs1use std::time::{Duration, Instant};
2
3use super::wasm_cache::{
4 WasmBuildError, WasmBuildOutcome, WasmBuildProgressConfig, WasmBuildProgressEvent,
5 WasmBuildSpec, build_wasm_canisters_cached, build_wasm_canisters_cached_with_progress,
6};
7
8#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct WasmBuildBatchOutcome {
11 outcomes: Vec<WasmBuildOutcome>,
12 total: Duration,
13}
14
15#[non_exhaustive]
17#[derive(Clone, Debug, Eq, PartialEq)]
18pub enum WasmBuildBatchProgressEvent {
19 BuildStarted {
21 index: usize,
23 total: usize,
25 },
26 BuildProgress {
28 index: usize,
30 event: WasmBuildProgressEvent,
32 },
33 BuildFinished {
35 index: usize,
37 },
38}
39
40#[derive(Debug)]
42pub struct WasmBuildBatchError {
43 failed_index: usize,
44 completed: Vec<WasmBuildOutcome>,
45 total: Duration,
46 source: WasmBuildError,
47}
48
49impl WasmBuildBatchOutcome {
50 #[must_use]
52 pub fn outcomes(&self) -> &[WasmBuildOutcome] {
53 &self.outcomes
54 }
55
56 #[must_use]
58 pub fn into_outcomes(self) -> Vec<WasmBuildOutcome> {
59 self.outcomes
60 }
61
62 #[must_use]
64 pub const fn total(&self) -> Duration {
65 self.total
66 }
67}
68
69impl WasmBuildBatchError {
70 #[must_use]
72 pub const fn failed_index(&self) -> usize {
73 self.failed_index
74 }
75
76 #[must_use]
78 pub fn completed(&self) -> &[WasmBuildOutcome] {
79 &self.completed
80 }
81
82 #[must_use]
84 pub fn into_parts(self) -> (Vec<WasmBuildOutcome>, WasmBuildError) {
85 (self.completed, self.source)
86 }
87
88 #[must_use]
90 pub const fn total(&self) -> Duration {
91 self.total
92 }
93}
94
95impl std::fmt::Display for WasmBuildBatchOutcome {
96 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 let reused = self
98 .outcomes
99 .iter()
100 .filter(|outcome| outcome.is_reused())
101 .count();
102 write!(
103 formatter,
104 "builds={} built={} reused={} total={:?}",
105 self.outcomes.len(),
106 self.outcomes.len().saturating_sub(reused),
107 reused,
108 self.total,
109 )
110 }
111}
112
113pub fn build_wasm_canisters_cached_batch(
121 specs: &[WasmBuildSpec],
122) -> Result<WasmBuildBatchOutcome, WasmBuildBatchError> {
123 build_wasm_batch(specs, |spec, _index| build_wasm_canisters_cached(spec))
124}
125
126pub fn build_wasm_canisters_cached_batch_with_progress<F>(
132 specs: &[WasmBuildSpec],
133 config: WasmBuildProgressConfig,
134 mut observer: F,
135) -> Result<WasmBuildBatchOutcome, WasmBuildBatchError>
136where
137 F: FnMut(WasmBuildBatchProgressEvent),
138{
139 let count = specs.len();
140 build_wasm_batch(specs, |spec, index| {
141 observer(WasmBuildBatchProgressEvent::BuildStarted {
142 index,
143 total: count,
144 });
145 let outcome = build_wasm_canisters_cached_with_progress(spec, config, |event| {
146 observer(WasmBuildBatchProgressEvent::BuildProgress { index, event });
147 })?;
148 observer(WasmBuildBatchProgressEvent::BuildFinished { index });
149 Ok(outcome)
150 })
151}
152
153fn build_wasm_batch<F>(
154 specs: &[WasmBuildSpec],
155 mut build: F,
156) -> Result<WasmBuildBatchOutcome, WasmBuildBatchError>
157where
158 F: FnMut(&WasmBuildSpec, usize) -> Result<WasmBuildOutcome, WasmBuildError>,
159{
160 let started = Instant::now();
161 let mut outcomes = Vec::with_capacity(specs.len());
162 for (index, spec) in specs.iter().enumerate() {
163 match build(spec, index) {
164 Ok(outcome) => outcomes.push(outcome),
165 Err(source) => {
166 return Err(WasmBuildBatchError {
167 failed_index: index,
168 completed: outcomes,
169 total: started.elapsed(),
170 source,
171 });
172 }
173 }
174 }
175 Ok(WasmBuildBatchOutcome {
176 outcomes,
177 total: started.elapsed(),
178 })
179}
180
181impl std::fmt::Display for WasmBuildBatchError {
182 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 write!(
184 formatter,
185 "independent Wasm build {} failed after {} successful build(s): {}",
186 self.failed_index,
187 self.completed.len(),
188 self.source,
189 )
190 }
191}
192
193impl std::error::Error for WasmBuildBatchError {
194 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
195 Some(&self.source)
196 }
197}
198
199#[cfg(test)]
200mod tests;