Skip to main content

rust_supervisor/tree/
builder.rs

1//! Supervisor tree builder.
2//!
3//! This module converts declarations into indexed tree nodes while preserving
4//! declaration order.
5
6use crate::error::types::SupervisorError;
7use crate::id::types::{ChildId, SupervisorPath};
8use crate::spec::child::{ChildSpec, TaskKind};
9use crate::spec::supervisor::SupervisorSpec;
10use std::collections::HashSet;
11
12/// Node in a supervisor tree.
13#[derive(Debug, Clone)]
14pub struct SupervisorTreeNode {
15    /// Path of the node in the tree.
16    pub path: SupervisorPath,
17    /// Child declaration attached to this node.
18    pub child: ChildSpec,
19    /// Zero-based declaration order under the parent.
20    pub declaration_index: usize,
21}
22
23/// Built supervisor tree with stable declaration order.
24#[derive(Debug, Clone)]
25pub struct SupervisorTree {
26    /// Root supervisor path.
27    pub root_path: SupervisorPath,
28    /// Nodes in declaration order.
29    pub nodes: Vec<SupervisorTreeNode>,
30}
31
32impl SupervisorTree {
33    /// Builds a tree from a supervisor specification.
34    ///
35    /// # Arguments
36    ///
37    /// - `spec`: Supervisor declaration to index.
38    ///
39    /// # Returns
40    ///
41    /// Returns a [`SupervisorTree`] when child identifiers and paths are valid.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// let spec = rust_supervisor::spec::supervisor::SupervisorSpec::root(Vec::new());
47    /// let tree = rust_supervisor::tree::builder::SupervisorTree::build(&spec).unwrap();
48    /// assert!(tree.nodes.is_empty());
49    /// ```
50    pub fn build(spec: &SupervisorSpec) -> Result<Self, SupervisorError> {
51        spec.validate()?;
52        let mut seen = HashSet::new();
53        let mut nodes = Vec::with_capacity(spec.children.len());
54        for (index, child) in spec.children.iter().enumerate() {
55            validate_child_path(&spec.path, &child.id, &mut seen)?;
56            nodes.push(SupervisorTreeNode {
57                path: spec.path.join(&child.id.value),
58                child: child.clone(),
59                declaration_index: index,
60            });
61        }
62        Ok(Self {
63            root_path: spec.path.clone(),
64            nodes,
65        })
66    }
67
68    /// Returns the path for a child identifier.
69    ///
70    /// # Arguments
71    ///
72    /// - `child_id`: Child identifier to locate.
73    ///
74    /// # Returns
75    ///
76    /// Returns the child path when the child exists.
77    pub fn child_path(&self, child_id: &ChildId) -> Option<SupervisorPath> {
78        self.nodes
79            .iter()
80            .find(|node| node.child.id == *child_id)
81            .map(|node| node.path.clone())
82    }
83
84    /// Returns supervisor nodes declared as nested supervisors.
85    ///
86    /// # Arguments
87    ///
88    /// This function has no arguments.
89    ///
90    /// # Returns
91    ///
92    /// Returns nested supervisor nodes in declaration order.
93    pub fn nested_supervisors(&self) -> Vec<&SupervisorTreeNode> {
94        self.nodes
95            .iter()
96            .filter(|node| node.child.kind == TaskKind::Supervisor)
97            .collect()
98    }
99}
100
101/// Validates path uniqueness for a child under a parent path.
102///
103/// # Arguments
104///
105/// - `parent`: Parent supervisor path.
106/// - `child_id`: Child identifier being appended to the path.
107/// - `seen`: Set of paths already declared under the parent.
108///
109/// # Returns
110///
111/// Returns `Ok(())` when the path is unique.
112fn validate_child_path(
113    parent: &SupervisorPath,
114    child_id: &ChildId,
115    seen: &mut HashSet<String>,
116) -> Result<(), SupervisorError> {
117    let path = parent.join(&child_id.value).to_string();
118    if seen.insert(path) {
119        Ok(())
120    } else {
121        Err(SupervisorError::fatal_config(format!(
122            "duplicate child path for {child_id}"
123        )))
124    }
125}