use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
use crate::model::{DependencyGraph, PipelineContract};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PipelinePlan {
pub contract_id: String,
pub contract_version: String,
pub step_order: Vec<String>,
}
pub fn plan(contract: &PipelineContract) -> PipelinePlan {
let declared_order: Vec<String> = contract.steps.iter().map(|step| step.id.clone()).collect();
let step_order = match DependencyGraph::from_contract(contract).topological_order() {
Ok(topo) if !topo.is_empty() => {
let topo_set: BTreeSet<String> = topo.iter().cloned().collect();
let mut order = topo;
for step_id in &declared_order {
if !topo_set.contains(step_id) {
order.push(step_id.clone());
}
}
order
}
_ => declared_order,
};
PipelinePlan {
contract_id: contract.id.clone(),
contract_version: contract.version.clone(),
step_order,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse_yaml;
#[test]
fn preserves_declaration_order_for_steps_missing_from_topo() {
let contract = parse_yaml(
r#"
dpcsVersion: "1.0.0"
id: "test"
version: "0.1.0"
interface:
inputs: []
outputs: []
steps:
- id: ""
type: "extension:noop"
- id: "a"
type: "extension:noop"
graph:
edges: []
"#,
)
.unwrap();
assert_eq!(plan(&contract).step_order, vec!["a", ""]);
}
}