use crate::id::types::ChildId;
use crate::spec::supervisor::{
GroupStrategy, StrategyExecutionPlan, SupervisionStrategy, SupervisorSpec,
};
use crate::tree::builder::{SupervisorTree, SupervisorTreeNode};
pub fn startup_order(tree: &SupervisorTree) -> Vec<&SupervisorTreeNode> {
tree.nodes.iter().collect()
}
pub fn shutdown_order(tree: &SupervisorTree) -> Vec<&SupervisorTreeNode> {
tree.nodes.iter().rev().collect()
}
pub fn restart_scope(
tree: &SupervisorTree,
strategy: SupervisionStrategy,
failed_child: &ChildId,
) -> Vec<ChildId> {
match strategy {
SupervisionStrategy::OneForOne => vec![failed_child.clone()],
SupervisionStrategy::OneForAll => all_children(tree),
SupervisionStrategy::RestForOne => rest_for_one(tree, failed_child),
}
}
pub fn restart_execution_plan(
tree: &SupervisorTree,
spec: &SupervisorSpec,
failed_child: &ChildId,
) -> StrategyExecutionPlan {
if let Some(override_strategy) = child_override(spec, failed_child) {
return StrategyExecutionPlan {
failed_child: failed_child.clone(),
strategy: override_strategy.strategy,
scope: restart_scope(tree, override_strategy.strategy, failed_child),
group: None,
restart_budget: override_strategy.restart_budget.or(spec.restart_budget),
escalation_policy: override_strategy
.escalation_policy
.or(spec.escalation_policy),
dynamic_supervisor_enabled: spec.dynamic_supervisor_policy.enabled,
};
}
if let Some(group_strategy) = group_strategy(tree, spec, failed_child) {
return StrategyExecutionPlan {
failed_child: failed_child.clone(),
strategy: group_strategy.strategy,
scope: group_restart_scope(
tree,
&group_strategy.group,
group_strategy.strategy,
failed_child,
),
group: Some(group_strategy.group.clone()),
restart_budget: group_strategy.restart_budget.or(spec.restart_budget),
escalation_policy: group_strategy.escalation_policy.or(spec.escalation_policy),
dynamic_supervisor_enabled: spec.dynamic_supervisor_policy.enabled,
};
}
StrategyExecutionPlan {
failed_child: failed_child.clone(),
strategy: spec.strategy,
scope: restart_scope(tree, spec.strategy, failed_child),
group: None,
restart_budget: spec.restart_budget,
escalation_policy: spec.escalation_policy,
dynamic_supervisor_enabled: spec.dynamic_supervisor_policy.enabled,
}
}
fn all_children(tree: &SupervisorTree) -> Vec<ChildId> {
tree.nodes
.iter()
.map(|node| node.child.id.clone())
.collect()
}
fn rest_for_one(tree: &SupervisorTree, failed_child: &ChildId) -> Vec<ChildId> {
let Some(index) = tree
.nodes
.iter()
.position(|node| node.child.id == *failed_child)
else {
return Vec::new();
};
tree.nodes[index..]
.iter()
.map(|node| node.child.id.clone())
.collect()
}
fn child_override<'a>(
spec: &'a SupervisorSpec,
failed_child: &ChildId,
) -> Option<&'a crate::spec::supervisor::ChildStrategyOverride> {
spec.child_strategy_overrides
.iter()
.find(|override_strategy| override_strategy.child_id == *failed_child)
}
fn group_strategy<'a>(
tree: &SupervisorTree,
spec: &'a SupervisorSpec,
failed_child: &ChildId,
) -> Option<&'a GroupStrategy> {
let child = tree
.nodes
.iter()
.find(|node| node.child.id == *failed_child)?;
spec.group_strategies
.iter()
.find(|strategy| child.child.tags.contains(&strategy.group))
}
fn group_restart_scope(
tree: &SupervisorTree,
group: &str,
strategy: SupervisionStrategy,
failed_child: &ChildId,
) -> Vec<ChildId> {
let group_nodes = group_nodes(tree, group);
match strategy {
SupervisionStrategy::OneForOne => vec![failed_child.clone()],
SupervisionStrategy::OneForAll => group_nodes
.iter()
.map(|node| node.child.id.clone())
.collect(),
SupervisionStrategy::RestForOne => group_rest_for_one(&group_nodes, failed_child),
}
}
fn group_nodes<'a>(tree: &'a SupervisorTree, group: &str) -> Vec<&'a SupervisorTreeNode> {
tree.nodes
.iter()
.filter(|node| node.child.tags.iter().any(|tag| tag == group))
.collect()
}
fn group_rest_for_one(nodes: &[&SupervisorTreeNode], failed_child: &ChildId) -> Vec<ChildId> {
let Some(index) = nodes.iter().position(|node| node.child.id == *failed_child) else {
return Vec::new();
};
nodes[index..]
.iter()
.map(|node| node.child.id.clone())
.collect()
}