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}