Skip to main content

foxtive_supervisor/
hierarchy.rs

1//! Supervisor hierarchy and nesting support
2//!
3//! This module provides the ability to create parent-child relationships between supervisors,
4//! enabling hierarchical task management and organized supervision trees.
5
6use crate::runtime::TaskRuntime;
7use crate::Supervisor;
8use std::sync::Arc;
9use tokio::sync::Mutex;
10use tracing::info;
11
12/// Builder for constructing supervisor hierarchies
13pub struct SupervisorHierarchy {
14    root: HierarchyBuilder,
15}
16
17/// Internal builder node - mutable during construction
18struct HierarchyBuilder {
19    supervisor: Option<Supervisor>,
20    children: Vec<HierarchyBuilder>,
21    id: String,
22}
23
24/// Running hierarchy - immutable tree of TaskRuntimes
25pub struct HierarchyRuntime {
26    root: Arc<RuntimeNode>,
27}
28
29/// A node in the running hierarchy tree
30pub struct RuntimeNode {
31    runtime: Mutex<Option<TaskRuntime>>,
32    children: Vec<Arc<RuntimeNode>>,
33    id: String,
34}
35
36impl HierarchyBuilder {
37    fn new(id: impl Into<String>) -> Self {
38        Self {
39            supervisor: None,
40            children: Vec::new(),
41            id: id.into(),
42        }
43    }
44
45    fn with_supervisor(mut self, supervisor: Supervisor) -> Self {
46        self.supervisor = Some(supervisor);
47        self
48    }
49
50    fn add_child(mut self, child: HierarchyBuilder) -> Self {
51        self.children.push(child);
52        self
53    }
54}
55
56impl SupervisorHierarchy {
57    /// Create a new supervisor hierarchy with a root node
58    pub fn new(root_id: impl Into<String>) -> Self {
59        Self {
60            root: HierarchyBuilder::new(root_id),
61        }
62    }
63
64    /// Add a child supervisor at the root level
65    pub fn add_child(mut self, id: impl Into<String>, supervisor: Supervisor) -> Self {
66        let child = HierarchyBuilder::new(id).with_supervisor(supervisor);
67        self.root = self.root.add_child(child);
68        self
69    }
70
71    /// Start all supervisors in the hierarchy (bottom-up)
72    ///
73    /// This consumes the builder and returns a running hierarchy.
74    /// Children are started before their parents to ensure dependencies are ready.
75    pub async fn start_all(self) -> Result<HierarchyRuntime, crate::error::SupervisorError> {
76        info!("Starting hierarchy from root '{}'", self.root.id);
77        let root = Box::pin(Self::start_builder_node(self.root)).await?;
78        Ok(HierarchyRuntime { root })
79    }
80
81    /// Recursively start a builder node and its children
82    async fn start_builder_node(
83        builder: HierarchyBuilder,
84    ) -> Result<Arc<RuntimeNode>, crate::error::SupervisorError> {
85        // Start children first (bottom-up)
86        let mut children = Vec::new();
87        for child_builder in builder.children {
88            let child_runtime = Box::pin(Self::start_builder_node(child_builder)).await?;
89            children.push(child_runtime);
90        }
91
92        // Start this supervisor if it has one
93        let runtime = if let Some(supervisor) = builder.supervisor {
94            info!(node_id = %builder.id, "Starting supervisor");
95            let rt = supervisor.start().await?;
96            info!(node_id = %builder.id, "Supervisor started with {} tasks", rt.task_count());
97            Some(rt)
98        } else {
99            None
100        };
101
102        Ok(Arc::new(RuntimeNode {
103            runtime: Mutex::new(runtime),
104            children,
105            id: builder.id,
106        }))
107    }
108}
109
110impl RuntimeNode {
111    /// Shutdown this node and all its children
112    async fn shutdown_node(self: Arc<Self>) {
113        info!(node_id = %self.id, "Initiating shutdown");
114
115        // Shutdown children in parallel
116        let shutdown_futures: Vec<_> = self.children.iter().map(|child| {
117            let child_clone = child.clone();
118            async move {
119                Box::pin(child_clone.shutdown_node()).await;
120            }
121        }).collect();
122
123        futures::future::join_all(shutdown_futures).await;
124
125        // Shutdown this node's runtime
126        let mut guard = self.runtime.lock().await;
127        if let Some(runtime) = guard.take() {
128            info!(node_id = %self.id, "Shutting down supervisor");
129            runtime.shutdown().await;
130            info!(node_id = %self.id, "Supervisor shut down");
131        }
132    }
133
134    /// Get total task count for this node and all descendants
135    fn task_count(&self) -> usize {
136        let local_count = match &*self.runtime.try_lock().unwrap() {
137            Some(rt) => rt.task_count(),
138            None => 0,
139        };
140
141        let child_count: usize = self.children.iter()
142            .map(|child| child.task_count())
143            .sum();
144
145        local_count + child_count
146    }
147
148    /// Find a node by ID
149    #[allow(dead_code)]
150    fn find_node(&self, target_id: &str) -> Option<&Self> {
151        if self.id == target_id {
152            return Some(self);
153        }
154
155        for child in &self.children {
156            if let Some(found) = child.find_node(target_id) {
157                return Some(found);
158            }
159        }
160
161        None
162    }
163}
164
165impl HierarchyRuntime {
166    /// Shutdown all supervisors in the hierarchy (top-down)
167    pub async fn shutdown_all(self) {
168        info!("Initiating hierarchy shutdown from root '{}'", self.root.id);
169        Box::pin(self.root.shutdown_node()).await;
170        info!("Hierarchy shutdown complete");
171    }
172
173    /// Get total task count across all nodes
174    pub fn total_task_count(&self) -> usize {
175        self.root.task_count()
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    #[tokio::test]
184    async fn test_create_hierarchy() {
185        let hierarchy = SupervisorHierarchy::new("root");
186        // Just verify it compiles and creates
187        let _ = hierarchy;
188    }
189}