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