re-uploading work

This commit is contained in:
2026-02-04 17:46:30 -06:00
commit 3b14c65998
1388 changed files with 381262 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
//! Message Publisher
//!
//! This module provides functionality for publishing messages to RabbitMQ exchanges.
//! It supports:
//! - Asynchronous message publishing
//! - Message confirmation (publisher confirms)
//! - Automatic routing based on message type
//! - Error handling and retries
use lapin::{
options::{BasicPublishOptions, ConfirmSelectOptions},
BasicProperties, Channel,
};
use tracing::{debug, info};
use super::{
error::{MqError, MqResult},
messages::MessageEnvelope,
Connection, DeliveryMode,
};
// Re-export for convenience
pub use super::config::PublisherConfig;
/// Message publisher for sending messages to RabbitMQ
pub struct Publisher {
/// RabbitMQ channel
channel: Channel,
/// Publisher configuration
config: PublisherConfig,
}
impl Publisher {
/// Create a new publisher from a connection
pub async fn new(connection: &Connection, config: PublisherConfig) -> MqResult<Self> {
let channel = connection.create_channel().await?;
// Enable publisher confirms if configured
if config.confirm_publish {
channel
.confirm_select(ConfirmSelectOptions::default())
.await
.map_err(|e| MqError::Channel(format!("Failed to enable confirms: {}", e)))?;
debug!("Publisher confirms enabled");
}
Ok(Self { channel, config })
}
/// Publish a message envelope to its designated exchange
pub async fn publish_envelope<T>(&self, envelope: &MessageEnvelope<T>) -> MqResult<()>
where
T: Clone + serde::Serialize + for<'de> serde::Deserialize<'de>,
{
let exchange = envelope.message_type.exchange();
let routing_key = envelope.message_type.routing_key();
self.publish_envelope_with_routing(envelope, &exchange, &routing_key)
.await
}
/// Publish a message envelope with explicit exchange and routing key
pub async fn publish_envelope_with_routing<T>(
&self,
envelope: &MessageEnvelope<T>,
exchange: &str,
routing_key: &str,
) -> MqResult<()>
where
T: Clone + serde::Serialize + for<'de> serde::Deserialize<'de>,
{
let payload = envelope
.to_bytes()
.map_err(|e| MqError::Serialization(format!("Failed to serialize envelope: {}", e)))?;
debug!(
"Publishing message {} to exchange '{}' with routing key '{}'",
envelope.message_id, exchange, routing_key
);
let properties = BasicProperties::default()
.with_delivery_mode(DeliveryMode::Persistent as u8)
.with_message_id(envelope.message_id.to_string().into())
.with_correlation_id(envelope.correlation_id.to_string().into())
.with_timestamp(envelope.timestamp.timestamp() as u64)
.with_content_type("application/json".into());
let confirmation = self
.channel
.basic_publish(
exchange,
routing_key,
BasicPublishOptions::default(),
&payload,
properties,
)
.await
.map_err(|e| MqError::Publish(format!("Failed to publish message: {}", e)))?;
// Wait for confirmation if enabled
if self.config.confirm_publish {
confirmation
.await
.map_err(|e| MqError::Publish(format!("Message not confirmed: {}", e)))?;
debug!("Message {} confirmed", envelope.message_id);
}
info!(
"Message {} published successfully to '{}'",
envelope.message_id, exchange
);
Ok(())
}
/// Publish a raw message with custom properties
pub async fn publish_raw(
&self,
exchange: &str,
routing_key: &str,
payload: &[u8],
properties: BasicProperties,
) -> MqResult<()> {
debug!(
"Publishing raw message to exchange '{}' with routing key '{}'",
exchange, routing_key
);
self.channel
.basic_publish(
exchange,
routing_key,
BasicPublishOptions::default(),
payload,
properties,
)
.await
.map_err(|e| MqError::Publish(format!("Failed to publish raw message: {}", e)))?;
Ok(())
}
/// Get the underlying channel
pub fn channel(&self) -> &Channel {
&self.channel
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(dead_code)]
struct TestPayload {
data: String,
}
#[test]
fn test_publisher_config_defaults() {
let config = PublisherConfig {
confirm_publish: true,
timeout_secs: 5,
exchange: "test.exchange".to_string(),
};
assert!(config.confirm_publish);
assert_eq!(config.timeout_secs, 5);
}
// Integration tests would require a running RabbitMQ instance
// and should be in a separate integration test file
}