use std::time::{Duration, Instant};
use super::wasm_cache::{
WasmBuildError, WasmBuildOutcome, WasmBuildProgressConfig, WasmBuildProgressEvent,
WasmBuildSpec, build_wasm_canisters_cached, build_wasm_canisters_cached_with_progress,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WasmBuildBatchOutcome {
outcomes: Vec<WasmBuildOutcome>,
total: Duration,
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum WasmBuildBatchProgressEvent {
BuildStarted {
index: usize,
total: usize,
},
BuildProgress {
index: usize,
event: WasmBuildProgressEvent,
},
BuildFinished {
index: usize,
},
}
#[derive(Debug)]
pub struct WasmBuildBatchError {
failed_index: usize,
completed: Vec<WasmBuildOutcome>,
total: Duration,
source: WasmBuildError,
}
impl WasmBuildBatchOutcome {
#[must_use]
pub fn outcomes(&self) -> &[WasmBuildOutcome] {
&self.outcomes
}
#[must_use]
pub fn into_outcomes(self) -> Vec<WasmBuildOutcome> {
self.outcomes
}
#[must_use]
pub const fn total(&self) -> Duration {
self.total
}
}
impl WasmBuildBatchError {
#[must_use]
pub const fn failed_index(&self) -> usize {
self.failed_index
}
#[must_use]
pub fn completed(&self) -> &[WasmBuildOutcome] {
&self.completed
}
#[must_use]
pub fn into_parts(self) -> (Vec<WasmBuildOutcome>, WasmBuildError) {
(self.completed, self.source)
}
#[must_use]
pub const fn total(&self) -> Duration {
self.total
}
}
impl std::fmt::Display for WasmBuildBatchOutcome {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let reused = self
.outcomes
.iter()
.filter(|outcome| outcome.is_reused())
.count();
write!(
formatter,
"builds={} built={} reused={} total={:?}",
self.outcomes.len(),
self.outcomes.len().saturating_sub(reused),
reused,
self.total,
)
}
}
pub fn build_wasm_canisters_cached_batch(
specs: &[WasmBuildSpec],
) -> Result<WasmBuildBatchOutcome, WasmBuildBatchError> {
build_wasm_batch(specs, |spec, _index| build_wasm_canisters_cached(spec))
}
pub fn build_wasm_canisters_cached_batch_with_progress<F>(
specs: &[WasmBuildSpec],
config: WasmBuildProgressConfig,
mut observer: F,
) -> Result<WasmBuildBatchOutcome, WasmBuildBatchError>
where
F: FnMut(WasmBuildBatchProgressEvent),
{
let count = specs.len();
build_wasm_batch(specs, |spec, index| {
observer(WasmBuildBatchProgressEvent::BuildStarted {
index,
total: count,
});
let outcome = build_wasm_canisters_cached_with_progress(spec, config, |event| {
observer(WasmBuildBatchProgressEvent::BuildProgress { index, event });
})?;
observer(WasmBuildBatchProgressEvent::BuildFinished { index });
Ok(outcome)
})
}
fn build_wasm_batch<F>(
specs: &[WasmBuildSpec],
mut build: F,
) -> Result<WasmBuildBatchOutcome, WasmBuildBatchError>
where
F: FnMut(&WasmBuildSpec, usize) -> Result<WasmBuildOutcome, WasmBuildError>,
{
let started = Instant::now();
let mut outcomes = Vec::with_capacity(specs.len());
for (index, spec) in specs.iter().enumerate() {
match build(spec, index) {
Ok(outcome) => outcomes.push(outcome),
Err(source) => {
return Err(WasmBuildBatchError {
failed_index: index,
completed: outcomes,
total: started.elapsed(),
source,
});
}
}
}
Ok(WasmBuildBatchOutcome {
outcomes,
total: started.elapsed(),
})
}
impl std::fmt::Display for WasmBuildBatchError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
formatter,
"independent Wasm build {} failed after {} successful build(s): {}",
self.failed_index,
self.completed.len(),
self.source,
)
}
}
impl std::error::Error for WasmBuildBatchError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
#[cfg(test)]
mod tests;