Skip to main content

camel_api/
lifecycle.rs

1use crate::{CamelError, MetricsCollector};
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4use std::sync::Arc;
5
6/// Status of a Lifecycle service.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8pub enum ServiceStatus {
9    Stopped,
10    Started,
11    Failed,
12}
13
14/// Aggregated system health status.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16pub enum HealthStatus {
17    Healthy,
18    /// Service is operational but with reduced capability.
19    Degraded,
20    Unhealthy,
21}
22
23/// Lifecycle trait for background services.
24///
25/// This trait follows Apache Camel's Service pattern but uses a different name
26/// to avoid confusion with tower::Service which is the core of rust-camel's
27/// request processing.
28///
29/// # Why `&mut self`?
30///
31/// The `start()` and `stop()` methods require `&mut self` to ensure:
32/// - **Exclusive access**: Prevents concurrent start/stop operations on the same service
33/// - **Safe state transitions**: Services can safely mutate their internal state
34/// - **No data races**: Compile-time guarantee of single-threaded access to service state
35///
36/// This design choice trades flexibility for safety - services cannot be started/stopped
37/// concurrently, which simplifies implementation and prevents race conditions.
38#[async_trait]
39pub trait Lifecycle: Send + Sync {
40    /// Service name for logging
41    fn name(&self) -> &str;
42
43    /// Start service (called during CamelContext.start())
44    async fn start(&mut self) -> Result<(), CamelError>;
45
46    /// Stop service (called during CamelContext.stop())
47    async fn stop(&mut self) -> Result<(), CamelError>;
48
49    /// Optional: expose MetricsCollector for auto-registration
50    fn as_metrics_collector(&self) -> Option<Arc<dyn MetricsCollector>> {
51        None
52    }
53
54    fn as_function_invoker(&self) -> Option<Arc<dyn crate::function::FunctionInvoker>> {
55        None
56    }
57
58    /// Current status of the service.
59    fn status(&self) -> ServiceStatus {
60        ServiceStatus::Stopped
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    struct TestService;
69
70    #[async_trait]
71    impl Lifecycle for TestService {
72        fn name(&self) -> &str {
73            "test"
74        }
75
76        async fn start(&mut self) -> Result<(), CamelError> {
77            Ok(())
78        }
79
80        async fn stop(&mut self) -> Result<(), CamelError> {
81            Ok(())
82        }
83    }
84
85    #[tokio::test]
86    async fn test_lifecycle_trait() {
87        let mut service = TestService;
88        assert_eq!(service.name(), "test");
89        service.start().await.unwrap();
90        service.stop().await.unwrap();
91    }
92
93    #[test]
94    fn test_default_status_is_stopped() {
95        let service = TestService;
96        assert_eq!(service.status(), ServiceStatus::Stopped);
97    }
98}