Skip to main content

anycms_event/
transport.rs

1//! Transport abstraction for distributed event bus.
2//!
3//! Allows plugging in different transport backends (Redis, Kafka, NATS, etc.)
4//!
5//! # Example
6//!
7//! ```ignore
8//! use anycms_event::transport::Transport;
9//!
10//! // Implement Transport for your backend
11//! struct KafkaTransport { /* ... */ }
12//!
13//! impl Transport for KafkaTransport {
14//!     fn publish(&self, event_name: &str, payload: &str) -> TransportFuture<'_> {
15//!         Box::pin(async move { /* publish to Kafka */ })
16//!     }
17//!     fn clone_box(&self) -> Box<dyn Transport> {
18//!         Box::new(self.clone())
19//!     }
20//! }
21//! ```
22
23use std::fmt::Debug;
24use std::future::Future;
25use std::pin::Pin;
26use std::sync::Arc;
27
28/// Type alias for the boxed future returned by Transport methods.
29pub type TransportFuture<'a> = Pin<Box<dyn Future<Output = Result<(), TransportError>> + Send + 'a>>;
30
31/// A message received from the transport layer.
32#[derive(Debug, Clone)]
33pub struct TransportMessage {
34    /// Event name / type identifier.
35    pub event_name: String,
36    /// Serialized event payload (JSON string).
37    pub payload: String,
38}
39
40/// Callback invoked when a message is received from the transport.
41pub type TransportMessageCallback = Arc<dyn Fn(TransportMessage) + Send + Sync>;
42
43/// Handle to an active subscription on a transport backend.
44///
45/// Implement this to provide lifecycle control over subscriptions.
46pub trait TransportSubscription: Send + Sync {
47    /// Stop receiving messages and clean up resources.
48    fn stop(&self);
49    /// Check if the subscription is still active.
50    fn is_active(&self) -> bool;
51}
52
53/// Error type for transport operations.
54#[derive(Debug, thiserror::Error)]
55pub enum TransportError {
56    /// Failed to connect to the transport backend.
57    #[error("Connection error: {0}")]
58    Connection(String),
59
60    /// Failed to publish a message.
61    #[error("Publish error: {0}")]
62    Publish(String),
63
64    /// Failed to subscribe to a channel.
65    #[error("Subscribe error: {0}")]
66    Subscribe(String),
67
68    /// Failed to serialize/deserialize a message.
69    #[error("Serialization error: {0}")]
70    Serialization(String),
71}
72
73/// Abstraction for transport backends that bridge EventBus instances across processes.
74///
75/// Implement this trait to add support for new messaging backends (Kafka, NATS, etc.)
76///
77/// # Trait Object Safety
78///
79/// This trait is object-safe and can be used as `dyn Transport` or `Box<dyn Transport>`.
80pub trait Transport: Send + Sync {
81    /// Publish a serialized event payload to the transport.
82    ///
83    /// # Arguments
84    /// * `event_name` - The event type identifier, used for routing
85    /// * `payload` - Pre-serialized JSON string of the event
86    fn publish(&self, event_name: &str, payload: &str) -> TransportFuture<'_>;
87
88    /// Subscribe to events matching `event_pattern` from the transport.
89    ///
90    /// The `callback` is invoked for each received message. Returns a
91    /// [`TransportSubscription`] handle for lifecycle control.
92    fn subscribe(
93        &self,
94        event_pattern: &str,
95        callback: TransportMessageCallback,
96    ) -> Result<Box<dyn TransportSubscription>, TransportError>;
97
98    /// Clone this transport into a boxed trait object.
99    ///
100    /// Required because trait objects cannot implement `Clone` directly.
101    /// Implement as `Box::new(self.clone())`.
102    fn clone_box(&self) -> Box<dyn Transport>;
103}
104
105/// Handle to a background forwarder task.
106///
107/// Implementors should provide a way to stop the forwarder and check its status.
108pub trait ForwarderHandle: Send + Sync {
109    /// Stop the forwarder task.
110    fn stop(&self);
111
112    /// Check if the forwarder has finished.
113    fn is_finished(&self) -> bool;
114}
115
116impl Debug for Box<dyn Transport> {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        f.debug_struct("Transport").finish()
119    }
120}
121
122impl Debug for Box<dyn TransportSubscription> {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        f.debug_struct("TransportSubscription").finish()
125    }
126}