use crate::model::{
artifacts::ArtifactChecksumRecord,
effect_graph::{EffectGraphError, EffectGraphRecord, EffectNodeRecord, MAX_EFFECT_OPERATIONS},
};
use std::collections::BTreeSet;
use thiserror::Error;
#[derive(Clone, Debug)]
pub struct EffectProgressRequest {
pub completed_operations: Vec<u64>,
}
#[derive(Clone, Debug)]
pub struct EffectReadinessView<'a> {
pub graph: ArtifactChecksumRecord,
pub completed_operations: usize,
pub ready: Vec<&'a EffectNodeRecord>,
pub blocked: Vec<&'a EffectNodeRecord>,
}
pub fn readiness<'a>(
graph: &'a EffectGraphRecord,
request: &EffectProgressRequest,
) -> Result<EffectReadinessView<'a>, EffectOrderError> {
if request.completed_operations.len() > MAX_EFFECT_OPERATIONS {
return Err(EffectOrderError::TooManyCompleted);
}
let mut completed = BTreeSet::new();
for sequence in &request.completed_operations {
graph.node(*sequence)?;
if !completed.insert(*sequence) {
return Err(EffectOrderError::DuplicateCompleted(*sequence));
}
}
for sequence in &completed {
let node = graph.node(*sequence)?;
if let Some(dependency) = node
.depends_on()
.iter()
.find(|dependency| !completed.contains(dependency))
{
return Err(EffectOrderError::UnmetDependency {
operation_sequence: *sequence,
dependency: *dependency,
});
}
}
let mut ready = Vec::new();
let mut blocked = Vec::new();
for node in graph
.ordered_nodes()
.filter(|node| !completed.contains(&node.operation_sequence()))
{
if node
.depends_on()
.iter()
.all(|dependency| completed.contains(dependency))
{
ready.push(node);
} else {
blocked.push(node);
}
}
Ok(EffectReadinessView {
graph: graph.digest(),
completed_operations: completed.len(),
ready,
blocked,
})
}
#[derive(Debug, Error)]
pub enum EffectOrderError {
#[error("completed effects exceed {MAX_EFFECT_OPERATIONS} operations")]
TooManyCompleted,
#[error("duplicate completed effect {0}")]
DuplicateCompleted(u64),
#[error("completed effect {operation_sequence} has unmet dependency {dependency}")]
UnmetDependency {
operation_sequence: u64,
dependency: u64,
},
#[error(transparent)]
Graph(#[from] EffectGraphError),
}
#[cfg(test)]
mod tests;