use crate::core::types::{ExecutionPlan, PlanAction};
use std::collections::HashSet;
#[derive(Debug, Clone, Default)]
pub struct PlanScope {
pairs: HashSet<(String, String)>,
machines: HashSet<String>,
}
impl PlanScope {
pub fn from_plan(plan: &ExecutionPlan) -> Self {
let mut scope = Self::default();
for change in &plan.changes {
if change.action == PlanAction::NoOp {
continue;
}
scope.machines.insert(change.machine.clone());
scope
.pairs
.insert((change.machine.clone(), change.resource_id.clone()));
}
scope
}
pub fn len(&self) -> usize {
self.pairs.len()
}
pub fn is_empty(&self) -> bool {
self.pairs.is_empty()
}
pub fn covers(&self, machine: &str, resource_id: &str) -> bool {
self.pairs
.contains(&(machine.to_string(), resource_id.to_string()))
}
pub fn covers_machine(&self, machine: &str) -> bool {
self.machines.contains(machine)
}
pub fn pairs(&self) -> impl Iterator<Item = (&str, &str)> {
self.pairs.iter().map(|(m, r)| (m.as_str(), r.as_str()))
}
pub fn machine_names(&self) -> Vec<String> {
let mut names: Vec<String> = self.machines.iter().cloned().collect();
names.sort();
names
}
}
fn recount(plan: &mut ExecutionPlan) {
plan.to_create = 0;
plan.to_update = 0;
plan.to_destroy = 0;
plan.unchanged = 0;
for change in &plan.changes {
match change.action {
PlanAction::Create => plan.to_create += 1,
PlanAction::Update => plan.to_update += 1,
PlanAction::Destroy => plan.to_destroy += 1,
PlanAction::NoOp => plan.unchanged += 1,
}
}
}
pub(crate) fn restrict(mut plan: ExecutionPlan, scope: Option<&PlanScope>) -> ExecutionPlan {
let Some(scope) = scope else {
return plan;
};
for change in &mut plan.changes {
if !scope.covers(&change.machine, &change.resource_id) {
change.action = PlanAction::NoOp;
}
}
recount(&mut plan);
plan
}
#[cfg(test)]
#[path = "tests_plan_scope.rs"]
mod tests_plan_scope;