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}