rust_rabbit/lib.rs
1//! # rust-rabbit 🐰
2//!
3//! A **simple, reliable** RabbitMQ client library for Rust.
4//! Focus on core functionality with minimal configuration.
5//!
6//! ## Features
7//!
8//! - **🚀 Simple API**: Just Publisher and Consumer with essential methods
9//! - **🔄 Flexible Retry**: Exponential, linear, or custom retry mechanisms
10//! - **🛠️ Auto-Setup**: Automatic queue/exchange declaration and binding
11//! - **⚡ Built-in Reliability**: Default ACK behavior with error handling
12//!
13//! ## Quick Start
14//!
15//! ### Publisher
16//!
17//! ```rust,no_run
18//! use rust_rabbit::{Connection, Publisher, PublishOptions};
19//! use serde::Serialize;
20//!
21//! #[derive(Serialize)]
22//! struct Order {
23//! id: u32,
24//! amount: f64,
25//! }
26//!
27//! #[tokio::main]
28//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
29//! let connection = Connection::new("amqp://localhost:5672").await?;
30//! let publisher = Publisher::new(connection);
31//!
32//! let order = Order { id: 123, amount: 99.99 };
33//!
34//! // Publish to exchange
35//! publisher.publish_to_exchange("orders", "new.order", &order, None).await?;
36//!
37//! // Publish directly to queue
38//! publisher.publish_to_queue("order_queue", &order, None).await?;
39//!
40//! Ok(())
41//! }
42//! ```
43//!
44//! ### Consumer with Retry
45//!
46//! ```rust,no_run
47//! use rust_rabbit::{Connection, Consumer, RetryConfig};
48//! use serde::Deserialize;
49//!
50//! #[derive(Deserialize)]
51//! struct Order {
52//! id: u32,
53//! amount: f64,
54//! }
55//!
56//! #[tokio::main]
57//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
58//! let connection = Connection::new("amqp://localhost:5672").await?;
59//!
60//! let consumer = Consumer::builder(connection, "order_queue")
61//! .retry(RetryConfig::exponential_default()) // 1s->2s->4s->8s->16s
62//! .bind_to_exchange("orders")
63//! .concurrency(5)
64//! .build()
65//! .await?;
66//!
67//! consumer.consume(|order: Order| async move {
68//! println!("Processing order {}: ${}", order.id, order.amount);
69//! // Your business logic here
70//! Ok(()) // ACK message
71//! }).await?;
72//!
73//! Ok(())
74//! }
75//! ```
76//!
77//! ## Retry Configurations
78//!
79//! ```rust,no_run
80//! use rust_rabbit::RetryConfig;
81//! use std::time::Duration;
82//!
83//! // Exponential: 1s -> 2s -> 4s -> 8s -> 16s (5 retries)
84//! let exponential = RetryConfig::exponential_default();
85//!
86//! // Custom exponential: 2s -> 4s -> 8s -> 16s -> 32s (with cap at 60s)
87//! let custom_exp = RetryConfig::exponential(5, Duration::from_secs(2), Duration::from_secs(60));
88//!
89//! // Linear: 10s -> 10s -> 10s (3 retries)
90//! let linear = RetryConfig::linear(3, Duration::from_secs(10));
91//!
92//! // Custom delays: 1s -> 5s -> 30s
93//! let custom = RetryConfig::custom(vec![
94//! Duration::from_secs(1),
95//! Duration::from_secs(5),
96//! Duration::from_secs(30),
97//! ]);
98//!
99//! // No retries
100//! let no_retry = RetryConfig::no_retry();
101//! ```
102
103// Re-export main types for easy access
104pub use connection::{Connection, ConnectionBuilder, ConnectionConfig};
105pub use consumer::{Consumer, ConsumerBuilder, Message};
106pub use error::{Result, RustRabbitError};
107pub use publisher::{PublishOptions, Publisher};
108pub use retry::{RetryConfig, RetryMechanism};
109
110// Internal modules
111mod connection;
112mod consumer;
113mod error;
114mod publisher;
115mod retry;
116
117/// Prelude module for convenient imports
118pub mod prelude {
119 pub use crate::{
120 Connection, Consumer, ConsumerBuilder, Message, PublishOptions, Publisher, Result,
121 RetryConfig, RetryMechanism, RustRabbitError,
122 };
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use serde::{Deserialize, Serialize};
129 use std::time::Duration;
130
131 #[derive(Debug, Serialize, Deserialize, PartialEq)]
132 #[allow(dead_code)]
133 struct TestMessage {
134 id: u32,
135 content: String,
136 }
137
138 #[tokio::test]
139 async fn test_api_compilation() {
140 // This test ensures the API compiles correctly
141 // Real integration tests would require a RabbitMQ instance
142
143 let _connection_result = Connection::new("amqp://localhost:5672").await;
144
145 // Test retry configurations
146 let _exponential = RetryConfig::exponential_default();
147 let _linear = RetryConfig::linear(3, Duration::from_secs(5));
148 let _custom = RetryConfig::custom(vec![Duration::from_secs(1), Duration::from_secs(5)]);
149 let _no_retry = RetryConfig::no_retry();
150 }
151
152 #[test]
153 fn test_basic_api_exists() {
154 // Test that our main types exist and can be referenced
155 use crate::prelude::*;
156
157 // This is a compile-time test - if it compiles, our API is accessible
158 let _: Option<Connection> = None;
159 let _: Option<Publisher> = None;
160 let _: Option<Consumer> = None;
161 let _: Option<RetryConfig> = None;
162
163 // Test that we can create basic configs
164 let _retry = RetryConfig::exponential_default();
165 let _options = PublishOptions::new();
166 }
167
168 #[test]
169 fn test_retry_config_calculations() {
170 let config = RetryConfig::exponential(5, Duration::from_secs(1), Duration::from_secs(30));
171
172 assert_eq!(config.calculate_delay(0), Some(Duration::from_secs(1)));
173 assert_eq!(config.calculate_delay(1), Some(Duration::from_secs(2)));
174 assert_eq!(config.calculate_delay(2), Some(Duration::from_secs(4)));
175 assert_eq!(config.calculate_delay(3), Some(Duration::from_secs(8)));
176 assert_eq!(config.calculate_delay(4), Some(Duration::from_secs(16)));
177 // Attempt 5 exceeds max_retries (5), so should return None
178 assert_eq!(config.calculate_delay(5), None);
179 }
180
181 #[test]
182 fn test_retry_config_linear() {
183 let config = RetryConfig::linear(3, Duration::from_secs(5));
184
185 assert_eq!(config.max_retries, 3);
186 assert_eq!(config.calculate_delay(0), Some(Duration::from_secs(5)));
187 assert_eq!(config.calculate_delay(1), Some(Duration::from_secs(5)));
188 assert_eq!(config.calculate_delay(2), Some(Duration::from_secs(5)));
189 assert_eq!(config.calculate_delay(3), None); // Exceeds max_retries
190 }
191
192 #[test]
193 fn test_retry_config_custom() {
194 let delays = vec![
195 Duration::from_secs(1),
196 Duration::from_secs(3),
197 Duration::from_secs(7),
198 ];
199 let config = RetryConfig::custom(delays.clone());
200
201 assert_eq!(config.max_retries, 3);
202 assert_eq!(config.calculate_delay(0), Some(Duration::from_secs(1)));
203 assert_eq!(config.calculate_delay(1), Some(Duration::from_secs(3)));
204 assert_eq!(config.calculate_delay(2), Some(Duration::from_secs(7)));
205 assert_eq!(config.calculate_delay(3), None); // Exceeds max_retries
206 }
207
208 #[test]
209 fn test_publish_options() {
210 let options = PublishOptions::new()
211 .mandatory()
212 .with_expiration("60000")
213 .with_priority(5);
214
215 assert!(options.mandatory);
216 assert_eq!(options.expiration, Some("60000".to_string()));
217 assert_eq!(options.priority, Some(5));
218 }
219}