server/service_bus_manager/types.rs
1use serde::{Deserialize, Serialize};
2
3/// Type of Service Bus queue for routing and processing messages.
4///
5/// Distinguishes between main queues (for normal message processing) and
6/// dead letter queues (for messages that cannot be processed successfully).
7///
8/// # Examples
9///
10/// ```no_run
11/// use quetty_server::service_bus_manager::QueueType;
12///
13/// let queue_type = QueueType::from_queue_name("my-queue");
14/// assert_eq!(queue_type, QueueType::Main);
15///
16/// let dlq_type = QueueType::from_queue_name("my-queue/$deadletterqueue");
17/// assert_eq!(dlq_type, QueueType::DeadLetter);
18/// ```
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub enum QueueType {
21 /// Main queue for normal message processing
22 Main,
23 /// Dead letter queue for failed messages
24 DeadLetter,
25}
26
27impl QueueType {
28 /// Determines the queue type from a queue name.
29 ///
30 /// Analyzes the queue name to determine if it's a dead letter queue
31 /// (ends with `/$deadletterqueue`) or a main queue.
32 ///
33 /// # Arguments
34 ///
35 /// * `queue_name` - The full queue name to analyze
36 ///
37 /// # Returns
38 ///
39 /// [`QueueType::DeadLetter`] if the name ends with `/$deadletterqueue`,
40 /// [`QueueType::Main`] otherwise
41 pub fn from_queue_name(queue_name: &str) -> Self {
42 if queue_name.ends_with("/$deadletterqueue") {
43 QueueType::DeadLetter
44 } else {
45 QueueType::Main
46 }
47 }
48}
49
50/// Information about a Service Bus queue including name and type.
51///
52/// Represents a queue with its full name and type classification. Provides
53/// utility methods for working with main queues and their corresponding
54/// dead letter queues.
55///
56/// # Examples
57///
58/// ```no_run
59/// use quetty_server::service_bus_manager::{QueueInfo, QueueType};
60///
61/// // Create a main queue
62/// let main_queue = QueueInfo::main_queue("orders".to_string());
63/// assert_eq!(main_queue.name, "orders");
64/// assert_eq!(main_queue.queue_type, QueueType::Main);
65///
66/// // Get the corresponding dead letter queue
67/// let dlq = main_queue.to_dlq();
68/// assert_eq!(dlq.name, "orders/$deadletterqueue");
69/// assert_eq!(dlq.queue_type, QueueType::DeadLetter);
70/// ```
71#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct QueueInfo {
73 /// Full name of the queue
74 pub name: String,
75 /// Type classification of the queue
76 pub queue_type: QueueType,
77}
78
79impl QueueInfo {
80 /// Creates a new QueueInfo with the specified name and type.
81 ///
82 /// # Arguments
83 ///
84 /// * `name` - The full queue name
85 /// * `queue_type` - The type of queue
86 ///
87 /// # Returns
88 ///
89 /// A new QueueInfo instance
90 pub fn new(name: String, queue_type: QueueType) -> Self {
91 Self { name, queue_type }
92 }
93
94 /// Creates a QueueInfo for a main queue.
95 ///
96 /// # Arguments
97 ///
98 /// * `name` - The base name of the queue
99 ///
100 /// # Returns
101 ///
102 /// A QueueInfo representing a main queue
103 pub fn main_queue(name: String) -> Self {
104 Self::new(name, QueueType::Main)
105 }
106
107 /// Creates a QueueInfo for a dead letter queue.
108 ///
109 /// Automatically appends the dead letter queue suffix to the base name.
110 ///
111 /// # Arguments
112 ///
113 /// * `base_name` - The base name of the queue (without DLQ suffix)
114 ///
115 /// # Returns
116 ///
117 /// A QueueInfo representing the dead letter queue
118 pub fn dead_letter_queue(base_name: String) -> Self {
119 let dlq_name = format!("{base_name}/$deadletterqueue");
120 Self::new(dlq_name, QueueType::DeadLetter)
121 }
122
123 /// Gets the base name of the queue without any type-specific suffixes.
124 ///
125 /// For main queues, returns the name as-is. For dead letter queues,
126 /// removes the `/$deadletterqueue` suffix to get the base name.
127 ///
128 /// # Returns
129 ///
130 /// The base queue name without type suffixes
131 pub fn base_name(&self) -> String {
132 match self.queue_type {
133 QueueType::Main => self.name.clone(),
134 QueueType::DeadLetter => {
135 if self.name.ends_with("/$deadletterqueue") {
136 self.name
137 .strip_suffix("/$deadletterqueue")
138 .map(|s| s.to_string())
139 .unwrap_or_else(|| {
140 log::warn!("Failed to strip DLQ suffix from queue name: {}", self.name);
141 self.name.clone()
142 })
143 } else {
144 self.name.clone()
145 }
146 }
147 }
148 }
149
150 /// Creates a QueueInfo for the dead letter queue corresponding to this queue.
151 ///
152 /// # Returns
153 ///
154 /// A QueueInfo representing the dead letter queue for this queue's base name
155 pub fn to_dlq(&self) -> Self {
156 Self::dead_letter_queue(self.base_name())
157 }
158
159 /// Creates a QueueInfo for the main queue corresponding to this queue.
160 ///
161 /// # Returns
162 ///
163 /// A QueueInfo representing the main queue for this queue's base name
164 pub fn to_main(&self) -> Self {
165 Self::main_queue(self.base_name())
166 }
167}
168
169/// Data structure for message content and metadata.
170///
171/// Represents a message to be sent to a Service Bus queue, including the
172/// message content and optional custom properties. Used for sending new
173/// messages and representing message data in transit.
174///
175/// # Examples
176///
177/// ```no_run
178/// use quetty_server::service_bus_manager::MessageData;
179/// use std::collections::HashMap;
180///
181/// // Simple message with just content
182/// let message = MessageData::new("Hello, world!".to_string());
183///
184/// // Message with custom properties
185/// let mut properties = HashMap::new();
186/// properties.insert("priority".to_string(), "high".to_string());
187/// properties.insert("source".to_string(), "orders-service".to_string());
188///
189/// let message = MessageData::with_properties(
190/// "Order processed: #12345".to_string(),
191/// properties
192/// );
193/// ```
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct MessageData {
196 /// The message content/body
197 pub content: String,
198 /// Optional custom properties for the message
199 pub properties: Option<std::collections::HashMap<String, String>>,
200}
201
202impl MessageData {
203 /// Creates a new MessageData with the specified content.
204 ///
205 /// # Arguments
206 ///
207 /// * `content` - The message content/body
208 ///
209 /// # Returns
210 ///
211 /// A new MessageData with no custom properties
212 pub fn new(content: String) -> Self {
213 Self {
214 content,
215 properties: None,
216 }
217 }
218
219 /// Creates a new MessageData with content and custom properties.
220 ///
221 /// # Arguments
222 ///
223 /// * `content` - The message content/body
224 /// * `properties` - Custom key-value properties for the message
225 ///
226 /// # Returns
227 ///
228 /// A new MessageData with the specified content and properties
229 pub fn with_properties(
230 content: String,
231 properties: std::collections::HashMap<String, String>,
232 ) -> Self {
233 Self {
234 content,
235 properties: Some(properties),
236 }
237 }
238}
239
240/// Statistics about Service Bus operations including success and failure counts.
241///
242/// Tracks the performance and outcome of Service Bus operations, providing
243/// metrics for monitoring and debugging purposes. Used throughout the system
244/// to report operation results.
245///
246/// # Examples
247///
248/// ```no_run
249/// use quetty_server::service_bus_manager::OperationStats;
250///
251/// let mut stats = OperationStats::new();
252/// stats.add_success();
253/// stats.add_success();
254/// stats.add_failure();
255///
256/// assert_eq!(stats.successful, 2);
257/// assert_eq!(stats.failed, 1);
258/// assert_eq!(stats.total, 3);
259/// assert_eq!(stats.success_rate(), 2.0 / 3.0);
260/// ```
261#[derive(Debug, Clone, Default)]
262pub struct OperationStats {
263 /// Number of successful operations
264 pub successful: usize,
265 /// Number of failed operations
266 pub failed: usize,
267 /// Total number of operations attempted
268 pub total: usize,
269}
270
271impl OperationStats {
272 pub fn new() -> Self {
273 Self::default()
274 }
275
276 pub fn add_success(&mut self) {
277 self.successful += 1;
278 self.total += 1;
279 }
280
281 pub fn add_failure(&mut self) {
282 self.failed += 1;
283 self.total += 1;
284 }
285
286 pub fn success_rate(&self) -> f64 {
287 if self.total == 0 {
288 0.0
289 } else {
290 self.successful as f64 / self.total as f64
291 }
292 }
293}