Skip to main content

camel_api/
route_controller.rs

1//! Route lifecycle management types.
2//!
3//! This module provides the [`RouteController`] trait for managing route lifecycle
4//! operations (start, stop, suspend, resume) plus [`RouteStatus`] and [`RouteAction`]
5//! enums used by runtime-facing APIs.
6
7use crate::CamelError;
8use async_trait::async_trait;
9
10/// Represents the current lifecycle status of a route.
11#[derive(Debug, Clone, PartialEq, Eq)]
12#[non_exhaustive]
13pub enum RouteStatus {
14    /// Route is stopped and not running.
15    Stopped,
16    /// Route is in the process of starting.
17    Starting,
18    /// Route is running and processing messages.
19    Started,
20    /// Route is in the process of stopping.
21    Stopping,
22    /// Route is suspended (temporarily paused).
23    Suspended,
24    /// Route has failed with an error message.
25    Failed(String),
26}
27
28/// Represents actions that can be performed on a route.
29#[derive(Debug, Clone)]
30#[non_exhaustive]
31pub enum RouteAction {
32    /// Start the route.
33    Start,
34    /// Stop the route.
35    Stop,
36    /// Suspend the route (pause without full shutdown).
37    Suspend,
38    /// Resume a suspended route.
39    Resume,
40    /// Restart the route (stop then start).
41    Restart,
42    /// Get the current status of the route.
43    Status,
44}
45
46/// Trait for managing route lifecycle operations.
47///
48/// Implementations provide imperative execution operations only; canonical
49/// lifecycle state is exposed via runtime query APIs.
50#[async_trait]
51pub trait RouteController: Send + Sync {
52    /// Start a specific route by its ID.
53    ///
54    /// # Errors
55    ///
56    /// Returns a `CamelError` if the route cannot be started.
57    async fn start_route(&mut self, route_id: &str) -> Result<(), CamelError>;
58
59    /// Stop a specific route by its ID.
60    ///
61    /// # Errors
62    ///
63    /// Returns a `CamelError` if the route cannot be stopped.
64    async fn stop_route(&mut self, route_id: &str) -> Result<(), CamelError>;
65
66    /// Stop a route with a graceful drain budget.
67    ///
68    /// Implementations may bound their drain phase by `budget`. The default
69    /// delegates to [`Self::stop_route`], ignoring the budget.
70    ///
71    /// # Errors
72    ///
73    /// Returns a `CamelError` if the route cannot be stopped.
74    async fn stop_route_with_budget(
75        &mut self,
76        route_id: &str,
77        _budget: std::time::Duration,
78    ) -> Result<(), CamelError> {
79        self.stop_route(route_id).await
80    }
81
82    /// Restart a specific route by its ID.
83    ///
84    /// # Errors
85    ///
86    /// Returns a `CamelError` if the route cannot be restarted.
87    async fn restart_route(&mut self, route_id: &str) -> Result<(), CamelError>;
88
89    /// Suspend a specific route by its ID.
90    ///
91    /// # Errors
92    ///
93    /// Returns a `CamelError` if the route cannot be suspended.
94    async fn suspend_route(&mut self, route_id: &str) -> Result<(), CamelError>;
95
96    /// Resume a suspended route by its ID.
97    ///
98    /// # Errors
99    ///
100    /// Returns a `CamelError` if the route cannot be resumed.
101    async fn resume_route(&mut self, route_id: &str) -> Result<(), CamelError>;
102
103    /// Start all routes in the context.
104    ///
105    /// # Errors
106    ///
107    /// Returns a `CamelError` if any route cannot be started.
108    async fn start_all_routes(&mut self) -> Result<(), CamelError>;
109
110    /// Stop all routes in the context.
111    ///
112    /// # Errors
113    ///
114    /// Returns a `CamelError` if any route cannot be stopped.
115    async fn stop_all_routes(&mut self) -> Result<(), CamelError>;
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use std::sync::{Arc, Mutex};
122
123    struct RecordingController {
124        stopped: Arc<Mutex<Vec<String>>>,
125    }
126
127    #[async_trait]
128    impl RouteController for RecordingController {
129        async fn start_route(&mut self, _route_id: &str) -> Result<(), CamelError> {
130            unimplemented!()
131        }
132
133        async fn stop_route(&mut self, route_id: &str) -> Result<(), CamelError> {
134            self.stopped
135                .lock()
136                .expect("stopped lock poisoned")
137                .push(route_id.to_string());
138            Ok(())
139        }
140
141        async fn restart_route(&mut self, _route_id: &str) -> Result<(), CamelError> {
142            unimplemented!()
143        }
144
145        async fn suspend_route(&mut self, _route_id: &str) -> Result<(), CamelError> {
146            unimplemented!()
147        }
148
149        async fn resume_route(&mut self, _route_id: &str) -> Result<(), CamelError> {
150            unimplemented!()
151        }
152
153        async fn start_all_routes(&mut self) -> Result<(), CamelError> {
154            unimplemented!()
155        }
156
157        async fn stop_all_routes(&mut self) -> Result<(), CamelError> {
158            unimplemented!()
159        }
160    }
161
162    #[tokio::test]
163    async fn stop_route_with_budget_default_delegates() {
164        let stopped = Arc::new(Mutex::new(Vec::new()));
165        let mut controller = RecordingController {
166            stopped: stopped.clone(),
167        };
168
169        let result = controller
170            .stop_route_with_budget("r1", std::time::Duration::from_secs(10))
171            .await;
172
173        assert!(result.is_ok(), "default impl must return Ok");
174        assert_eq!(*stopped.lock().expect("stopped lock poisoned"), vec!["r1"]);
175    }
176}