Skip to main content

fakecloud_core/
delivery.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4/// Cross-service message delivery.
5///
6/// Services use this to deliver messages to other services without
7/// direct dependencies between service crates. The server wires up
8/// the delivery functions at startup.
9pub struct DeliveryBus {
10    /// Deliver a message to an SQS queue by ARN.
11    sqs_sender: Option<Arc<dyn SqsDelivery>>,
12    /// Publish a message to an SNS topic by ARN.
13    sns_sender: Option<Arc<dyn SnsDelivery>>,
14    /// Put an event onto an EventBridge bus.
15    eventbridge_sender: Option<Arc<dyn EventBridgeDelivery>>,
16    /// Invoke a Lambda function by ARN.
17    lambda_invoker: Option<Arc<dyn LambdaDelivery>>,
18}
19
20/// Message attribute for SQS delivery from SNS.
21#[derive(Debug, Clone)]
22pub struct SqsMessageAttribute {
23    pub data_type: String,
24    pub string_value: Option<String>,
25    pub binary_value: Option<String>,
26}
27
28/// Trait for delivering messages to SQS queues.
29pub trait SqsDelivery: Send + Sync {
30    fn deliver_to_queue(
31        &self,
32        queue_arn: &str,
33        message_body: &str,
34        attributes: &HashMap<String, String>,
35    );
36
37    /// Deliver with message attributes and FIFO fields
38    fn deliver_to_queue_with_attrs(
39        &self,
40        queue_arn: &str,
41        message_body: &str,
42        message_attributes: &HashMap<String, SqsMessageAttribute>,
43        message_group_id: Option<&str>,
44        message_dedup_id: Option<&str>,
45    ) {
46        // Default implementation: fall back to simple delivery
47        let _ = (message_attributes, message_group_id, message_dedup_id);
48        self.deliver_to_queue(queue_arn, message_body, &HashMap::new());
49    }
50}
51
52/// Trait for publishing messages to SNS topics.
53pub trait SnsDelivery: Send + Sync {
54    fn publish_to_topic(&self, topic_arn: &str, message: &str, subject: Option<&str>);
55}
56
57/// Trait for putting events onto an EventBridge bus from cross-service integrations.
58pub trait EventBridgeDelivery: Send + Sync {
59    /// Put an event onto the specified event bus.
60    /// The implementation should handle rule matching and target delivery.
61    fn put_event(&self, source: &str, detail_type: &str, detail: &str, event_bus_name: &str);
62}
63
64/// Trait for invoking Lambda functions from cross-service integrations.
65pub trait LambdaDelivery: Send + Sync {
66    /// Invoke a Lambda function with the given payload.
67    /// The function is identified by ARN. Returns the response bytes on success.
68    fn invoke_lambda(
69        &self,
70        function_arn: &str,
71        payload: &str,
72    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Vec<u8>, String>> + Send>>;
73}
74
75impl DeliveryBus {
76    pub fn new() -> Self {
77        Self {
78            sqs_sender: None,
79            sns_sender: None,
80            eventbridge_sender: None,
81            lambda_invoker: None,
82        }
83    }
84
85    pub fn with_sqs(mut self, sender: Arc<dyn SqsDelivery>) -> Self {
86        self.sqs_sender = Some(sender);
87        self
88    }
89
90    pub fn with_sns(mut self, sender: Arc<dyn SnsDelivery>) -> Self {
91        self.sns_sender = Some(sender);
92        self
93    }
94
95    pub fn with_eventbridge(mut self, sender: Arc<dyn EventBridgeDelivery>) -> Self {
96        self.eventbridge_sender = Some(sender);
97        self
98    }
99
100    pub fn with_lambda(mut self, invoker: Arc<dyn LambdaDelivery>) -> Self {
101        self.lambda_invoker = Some(invoker);
102        self
103    }
104
105    /// Send a message to an SQS queue identified by ARN.
106    pub fn send_to_sqs(
107        &self,
108        queue_arn: &str,
109        message_body: &str,
110        attributes: &HashMap<String, String>,
111    ) {
112        if let Some(ref sender) = self.sqs_sender {
113            sender.deliver_to_queue(queue_arn, message_body, attributes);
114        }
115    }
116
117    /// Send a message to an SQS queue with message attributes and FIFO fields.
118    pub fn send_to_sqs_with_attrs(
119        &self,
120        queue_arn: &str,
121        message_body: &str,
122        message_attributes: &HashMap<String, SqsMessageAttribute>,
123        message_group_id: Option<&str>,
124        message_dedup_id: Option<&str>,
125    ) {
126        if let Some(ref sender) = self.sqs_sender {
127            sender.deliver_to_queue_with_attrs(
128                queue_arn,
129                message_body,
130                message_attributes,
131                message_group_id,
132                message_dedup_id,
133            );
134        }
135    }
136
137    /// Publish a message to an SNS topic identified by ARN.
138    pub fn publish_to_sns(&self, topic_arn: &str, message: &str, subject: Option<&str>) {
139        if let Some(ref sender) = self.sns_sender {
140            sender.publish_to_topic(topic_arn, message, subject);
141        }
142    }
143
144    /// Put an event onto an EventBridge bus.
145    pub fn put_event_to_eventbridge(
146        &self,
147        source: &str,
148        detail_type: &str,
149        detail: &str,
150        event_bus_name: &str,
151    ) {
152        if let Some(ref sender) = self.eventbridge_sender {
153            sender.put_event(source, detail_type, detail, event_bus_name);
154        }
155    }
156
157    /// Invoke a Lambda function identified by ARN.
158    pub async fn invoke_lambda(
159        &self,
160        function_arn: &str,
161        payload: &str,
162    ) -> Option<Result<Vec<u8>, String>> {
163        if let Some(ref invoker) = self.lambda_invoker {
164            Some(invoker.invoke_lambda(function_arn, payload).await)
165        } else {
166            None
167        }
168    }
169}
170
171impl Default for DeliveryBus {
172    fn default() -> Self {
173        Self::new()
174    }
175}