Skip to main content

flare_core_runtime/signal/
composite.rs

1//! 多信号合并器实现
2
3use super::ShutdownSignal;
4use futures::future::select_all;
5use std::pin::Pin;
6
7/// 多信号合并器
8///
9/// 监听多个信号源,任一触发即返回
10///
11/// # 示例
12///
13/// ```rust
14/// use flare_core_runtime::signal::{CompositeSignal, CtrlCSignal, ShutdownSignal};
15///
16/// let signal = CompositeSignal::new()
17///     .add(Box::new(CtrlCSignal::new()));
18///
19/// // 或者
20/// let signal = CompositeSignal::from_signals(vec![
21///     Box::new(CtrlCSignal::new()),
22/// ]);
23/// ```
24pub struct CompositeSignal {
25    signals: Vec<Box<dyn ShutdownSignal>>,
26    name: String,
27}
28
29impl CompositeSignal {
30    /// 创建新的多信号合并器
31    pub fn new() -> Self {
32        Self {
33            signals: Vec::new(),
34            name: "composite".to_string(),
35        }
36    }
37
38    /// 从信号列表创建
39    pub fn from_signals(signals: Vec<Box<dyn ShutdownSignal>>) -> Self {
40        Self {
41            signals,
42            name: "composite".to_string(),
43        }
44    }
45
46    /// 添加信号
47    #[allow(clippy::should_implement_trait)]
48    pub fn add(mut self, signal: Box<dyn ShutdownSignal>) -> Self {
49        self.signals.push(signal);
50        self
51    }
52
53    /// 设置名称
54    pub fn with_name(mut self, name: impl Into<String>) -> Self {
55        self.name = name.into();
56        self
57    }
58}
59
60impl Default for CompositeSignal {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66impl ShutdownSignal for CompositeSignal {
67    fn wait(&mut self) -> Pin<Box<dyn std::future::Future<Output = ()> + Send + '_>> {
68        if self.signals.is_empty() {
69            // 没有信号源,直接等待
70            return Box::pin(std::future::pending::<()>());
71        }
72
73        // 使用 select_all 等待任一信号
74        let futures: Vec<_> = self.signals.iter_mut().map(|s| s.wait()).collect();
75
76        if futures.is_empty() {
77            return Box::pin(std::future::pending::<()>());
78        }
79
80        // 使用 select_all 等待第一个触发的信号
81        Box::pin(async move {
82            let (result, _index, _others) = select_all(futures).await;
83            result
84        })
85    }
86
87    fn name(&self) -> &str {
88        &self.name
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use crate::signal::CtrlCSignal;
96
97    #[test]
98    fn test_composite_signal_new() {
99        let signal = CompositeSignal::new();
100        assert_eq!(signal.name(), "composite");
101    }
102
103    #[test]
104    fn test_composite_signal_add() {
105        let signal = CompositeSignal::new()
106            .add(Box::new(CtrlCSignal::new()))
107            .add(Box::new(CtrlCSignal::new()));
108
109        assert_eq!(signal.name(), "composite");
110    }
111
112    #[test]
113    fn test_composite_signal_from_signals() {
114        let signal = CompositeSignal::from_signals(vec![
115            Box::new(CtrlCSignal::new()),
116            Box::new(CtrlCSignal::new()),
117        ]);
118
119        assert_eq!(signal.name(), "composite");
120    }
121}