Skip to main content

a3s_boot/transport/
mod.rs

1use crate::{
2    validate_value, BootError, BoxFuture, ExecutionContext, ExecutionInterceptor,
3    ExecutionTransportKind, Guard, Result, Validate,
4};
5use serde::de::DeserializeOwned;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::future::Future;
9use std::ops::Deref;
10use std::sync::Arc;
11
12#[cfg(feature = "grpc-transport")]
13mod grpc;
14#[cfg(feature = "kafka-transport")]
15mod kafka;
16#[cfg(feature = "mqtt-transport")]
17mod mqtt;
18#[cfg(feature = "nats-transport")]
19mod nats;
20#[cfg(feature = "rabbitmq-transport")]
21mod rabbitmq;
22#[cfg(feature = "redis-transport")]
23mod redis;
24#[cfg(feature = "tcp-transport")]
25mod tcp;
26
27#[cfg(feature = "grpc-transport")]
28pub use self::grpc::{GrpcTransport, GrpcTransportClient, GrpcTransportOptions};
29#[cfg(feature = "kafka-transport")]
30pub use self::kafka::{KafkaTransport, KafkaTransportClient, KafkaTransportOptions};
31#[cfg(feature = "mqtt-transport")]
32pub use self::mqtt::{MqttTransport, MqttTransportClient, MqttTransportOptions, MqttTransportQoS};
33#[cfg(feature = "nats-transport")]
34pub use self::nats::{NatsTransport, NatsTransportClient, NatsTransportOptions};
35#[cfg(feature = "rabbitmq-transport")]
36pub use self::rabbitmq::{RabbitMqTransport, RabbitMqTransportClient, RabbitMqTransportOptions};
37#[cfg(feature = "redis-transport")]
38pub use self::redis::{RedisTransport, RedisTransportClient, RedisTransportOptions};
39#[cfg(feature = "tcp-transport")]
40pub use tcp::{TcpTransport, TcpTransportClient, TcpTransportOptions};
41
42/// Adapter-neutral microservice transport message.
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct TransportMessage {
45    pub pattern: String,
46    #[serde(default)]
47    pub data: Value,
48}
49
50impl TransportMessage {
51    pub fn new(pattern: impl Into<String>, data: impl Into<Value>) -> Self {
52        Self {
53            pattern: pattern.into(),
54            data: data.into(),
55        }
56    }
57
58    pub fn pattern(&self) -> &str {
59        &self.pattern
60    }
61
62    pub fn data(&self) -> &Value {
63        &self.data
64    }
65
66    pub fn text(pattern: impl Into<String>, data: impl Into<String>) -> Self {
67        Self::new(pattern, Value::String(data.into()))
68    }
69
70    pub fn json<T>(pattern: impl Into<String>, data: &T) -> Result<Self>
71    where
72        T: Serialize,
73    {
74        Ok(Self::new(
75            pattern,
76            serde_json::to_value(data).map_err(|err| BootError::Internal(err.to_string()))?,
77        ))
78    }
79
80    pub fn data_as<T>(&self) -> Result<T>
81    where
82        T: DeserializeOwned,
83    {
84        serde_json::from_value(self.data.clone())
85            .map_err(|err| BootError::BadRequest(err.to_string()))
86    }
87
88    pub fn validated_data<T>(&self) -> Result<T>
89    where
90        T: DeserializeOwned + Validate,
91    {
92        validate_value(self.data_as::<T>()?)
93    }
94}
95
96/// Reply returned by request-response message pattern handlers.
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
98pub struct TransportReply {
99    #[serde(default)]
100    pub data: Value,
101}
102
103impl TransportReply {
104    pub fn new(data: impl Into<Value>) -> Self {
105        Self { data: data.into() }
106    }
107
108    pub fn data(&self) -> &Value {
109        &self.data
110    }
111
112    pub fn text(data: impl Into<String>) -> Self {
113        Self::new(Value::String(data.into()))
114    }
115
116    pub fn json<T>(data: &T) -> Result<Self>
117    where
118        T: Serialize,
119    {
120        Ok(Self::new(
121            serde_json::to_value(data).map_err(|err| BootError::Internal(err.to_string()))?,
122        ))
123    }
124
125    pub fn data_as<T>(&self) -> Result<T>
126    where
127        T: DeserializeOwned,
128    {
129        serde_json::from_value(self.data.clone())
130            .map_err(|err| BootError::BadRequest(err.to_string()))
131    }
132}
133
134/// Return value accepted by request-response message pattern handlers.
135pub trait IntoTransportReply {
136    fn into_transport_reply(self) -> Option<TransportReply>;
137}
138
139impl IntoTransportReply for TransportReply {
140    fn into_transport_reply(self) -> Option<TransportReply> {
141        Some(self)
142    }
143}
144
145impl IntoTransportReply for Option<TransportReply> {
146    fn into_transport_reply(self) -> Option<TransportReply> {
147        self
148    }
149}
150
151impl IntoTransportReply for Value {
152    fn into_transport_reply(self) -> Option<TransportReply> {
153        Some(TransportReply::new(self))
154    }
155}
156
157impl IntoTransportReply for Option<Value> {
158    fn into_transport_reply(self) -> Option<TransportReply> {
159        self.map(TransportReply::new)
160    }
161}
162
163impl IntoTransportReply for () {
164    fn into_transport_reply(self) -> Option<TransportReply> {
165        None
166    }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub enum MessagePatternKind {
171    RequestResponse,
172    Event,
173}
174
175impl MessagePatternKind {
176    pub fn as_str(self) -> &'static str {
177        match self {
178            Self::RequestResponse => "request-response",
179            Self::Event => "event",
180        }
181    }
182}
183
184/// Context available to transport guards and interceptors.
185#[derive(Debug, Clone)]
186pub struct TransportContext {
187    pub pattern: String,
188    pub kind: MessagePatternKind,
189    pub module_name: Option<String>,
190    execution_context: ExecutionContext,
191}
192
193impl TransportContext {
194    fn new(definition: &MessagePatternDefinition, pattern: &str) -> Self {
195        let pattern = pattern.to_string();
196        let kind = definition.kind;
197        let module_name = definition.module_name.clone();
198        let execution_context = ExecutionContext::transport(
199            pattern.clone(),
200            ExecutionTransportKind::from(kind),
201            module_name.clone(),
202        );
203        Self {
204            pattern,
205            kind,
206            module_name,
207            execution_context,
208        }
209    }
210
211    pub fn execution_context(&self) -> &ExecutionContext {
212        &self.execution_context
213    }
214
215    pub fn into_execution_context(self) -> ExecutionContext {
216        self.execution_context
217    }
218}
219
220impl Deref for TransportContext {
221    type Target = ExecutionContext;
222
223    fn deref(&self) -> &Self::Target {
224        self.execution_context()
225    }
226}
227
228impl From<MessagePatternKind> for ExecutionTransportKind {
229    fn from(kind: MessagePatternKind) -> Self {
230        match kind {
231            MessagePatternKind::RequestResponse => Self::RequestResponse,
232            MessagePatternKind::Event => Self::Event,
233        }
234    }
235}
236
237/// Message transformation hook for transport message patterns.
238pub trait TransportPipe: Send + Sync + 'static {
239    fn transform(&self, message: TransportMessage) -> BoxFuture<'static, Result<TransportMessage>>;
240}
241
242impl<F, Fut> TransportPipe for F
243where
244    F: Fn(TransportMessage) -> Fut + Send + Sync + 'static,
245    Fut: Future<Output = Result<TransportMessage>> + Send + 'static,
246{
247    fn transform(&self, message: TransportMessage) -> BoxFuture<'static, Result<TransportMessage>> {
248        Box::pin(self(message))
249    }
250}
251
252/// Authorization hook for transport message patterns.
253pub trait TransportGuard: Send + Sync + 'static {
254    fn can_activate(&self, context: TransportContext) -> BoxFuture<'static, Result<bool>>;
255}
256
257impl<F, Fut> TransportGuard for F
258where
259    F: Fn(TransportContext) -> Fut + Send + Sync + 'static,
260    Fut: Future<Output = Result<bool>> + Send + 'static,
261{
262    fn can_activate(&self, context: TransportContext) -> BoxFuture<'static, Result<bool>> {
263        Box::pin(self(context))
264    }
265}
266
267struct ExecutionTransportGuard<G> {
268    inner: G,
269}
270
271impl<G> TransportGuard for ExecutionTransportGuard<G>
272where
273    G: Guard,
274{
275    fn can_activate(&self, context: TransportContext) -> BoxFuture<'static, Result<bool>> {
276        self.inner.can_activate(context.into_execution_context())
277    }
278}
279
280/// Around-handler hook for transport message patterns.
281pub trait TransportInterceptor: Send + Sync + 'static {
282    fn before(&self, _context: TransportContext) -> BoxFuture<'static, Result<()>> {
283        Box::pin(async { Ok(()) })
284    }
285
286    fn after(
287        &self,
288        _context: TransportContext,
289        reply: Option<TransportReply>,
290    ) -> BoxFuture<'static, Result<Option<TransportReply>>> {
291        Box::pin(async move { Ok(reply) })
292    }
293}
294
295struct ExecutionTransportInterceptor<I> {
296    inner: I,
297}
298
299impl<I> TransportInterceptor for ExecutionTransportInterceptor<I>
300where
301    I: ExecutionInterceptor,
302{
303    fn before(&self, context: TransportContext) -> BoxFuture<'static, Result<()>> {
304        self.inner.before(context.into_execution_context())
305    }
306
307    fn after(
308        &self,
309        context: TransportContext,
310        reply: Option<TransportReply>,
311    ) -> BoxFuture<'static, Result<Option<TransportReply>>> {
312        let future = self.inner.after(context.into_execution_context());
313        Box::pin(async move {
314            future.await?;
315            Ok(reply)
316        })
317    }
318}
319
320type TransportHandlerFuture = BoxFuture<'static, Result<Option<TransportReply>>>;
321type MessageValidator = Arc<dyn Fn(&TransportMessage) -> Result<()> + Send + Sync>;
322
323trait TransportMessageHandler: Send + Sync + 'static {
324    fn call(&self, message: TransportMessage) -> TransportHandlerFuture;
325}
326
327struct TransportHandlerAdapter<H> {
328    handler: H,
329}
330
331impl<H, Fut, R> TransportMessageHandler for TransportHandlerAdapter<H>
332where
333    H: Fn(TransportMessage) -> Fut + Send + Sync + 'static,
334    Fut: Future<Output = Result<R>> + Send + 'static,
335    R: IntoTransportReply + Send + 'static,
336{
337    fn call(&self, message: TransportMessage) -> TransportHandlerFuture {
338        let future = (self.handler)(message);
339        Box::pin(async move { Ok(future.await?.into_transport_reply()) })
340    }
341}
342
343/// Framework-neutral message pattern handler definition.
344#[derive(Clone)]
345pub struct MessagePatternDefinition {
346    pattern: String,
347    kind: MessagePatternKind,
348    handler: Arc<dyn TransportMessageHandler>,
349    pipes: Vec<Arc<dyn TransportPipe>>,
350    guards: Vec<Arc<dyn TransportGuard>>,
351    interceptors: Vec<Arc<dyn TransportInterceptor>>,
352    validators: Vec<MessageValidator>,
353    module_name: Option<String>,
354}
355
356impl MessagePatternDefinition {
357    pub fn request<H, Fut, R>(pattern: impl Into<String>, handler: H) -> Result<Self>
358    where
359        H: Fn(TransportMessage) -> Fut + Send + Sync + 'static,
360        Fut: Future<Output = Result<R>> + Send + 'static,
361        R: IntoTransportReply + Send + 'static,
362    {
363        Self::new(pattern, MessagePatternKind::RequestResponse, handler)
364    }
365
366    pub fn event<H, Fut>(pattern: impl Into<String>, handler: H) -> Result<Self>
367    where
368        H: Fn(TransportMessage) -> Fut + Send + Sync + 'static,
369        Fut: Future<Output = Result<()>> + Send + 'static,
370    {
371        Self::new(pattern, MessagePatternKind::Event, handler)
372    }
373
374    pub fn request_json<T, H, Fut, R>(pattern: impl Into<String>, handler: H) -> Result<Self>
375    where
376        T: DeserializeOwned + Send + 'static,
377        H: Fn(T) -> Fut + Send + Sync + 'static,
378        Fut: Future<Output = Result<R>> + Send + 'static,
379        R: Serialize + Send + 'static,
380    {
381        Self::request(pattern, move |message: TransportMessage| {
382            let payload = message.data_as::<T>();
383            let future = payload.map(&handler);
384            async move {
385                let response = future?.await?;
386                TransportReply::json(&response)
387            }
388        })
389    }
390
391    pub fn request_validated_json<T, H, Fut, R>(
392        pattern: impl Into<String>,
393        handler: H,
394    ) -> Result<Self>
395    where
396        T: DeserializeOwned + Validate + Send + 'static,
397        H: Fn(T) -> Fut + Send + Sync + 'static,
398        Fut: Future<Output = Result<R>> + Send + 'static,
399        R: Serialize + Send + 'static,
400    {
401        Self::request(pattern, move |message: TransportMessage| {
402            let payload = message.validated_data::<T>();
403            let future = payload.map(&handler);
404            async move {
405                let response = future?.await?;
406                TransportReply::json(&response)
407            }
408        })
409    }
410
411    pub fn event_json<T, H, Fut>(pattern: impl Into<String>, handler: H) -> Result<Self>
412    where
413        T: DeserializeOwned + Send + 'static,
414        H: Fn(T) -> Fut + Send + Sync + 'static,
415        Fut: Future<Output = Result<()>> + Send + 'static,
416    {
417        Self::event(pattern, move |message: TransportMessage| {
418            let payload = message.data_as::<T>();
419            let future = payload.map(&handler);
420            async move { future?.await }
421        })
422    }
423
424    pub fn event_validated_json<T, H, Fut>(pattern: impl Into<String>, handler: H) -> Result<Self>
425    where
426        T: DeserializeOwned + Validate + Send + 'static,
427        H: Fn(T) -> Fut + Send + Sync + 'static,
428        Fut: Future<Output = Result<()>> + Send + 'static,
429    {
430        Self::event(pattern, move |message: TransportMessage| {
431            let payload = message.validated_data::<T>();
432            let future = payload.map(&handler);
433            async move { future?.await }
434        })
435    }
436
437    fn new<H, Fut, R>(
438        pattern: impl Into<String>,
439        kind: MessagePatternKind,
440        handler: H,
441    ) -> Result<Self>
442    where
443        H: Fn(TransportMessage) -> Fut + Send + Sync + 'static,
444        Fut: Future<Output = Result<R>> + Send + 'static,
445        R: IntoTransportReply + Send + 'static,
446    {
447        let pattern = pattern.into();
448        validate_pattern(&pattern)?;
449        Ok(Self {
450            pattern,
451            kind,
452            handler: Arc::new(TransportHandlerAdapter { handler }),
453            pipes: Vec::new(),
454            guards: Vec::new(),
455            interceptors: Vec::new(),
456            validators: Vec::new(),
457            module_name: None,
458        })
459    }
460
461    pub fn pattern(&self) -> &str {
462        &self.pattern
463    }
464
465    pub fn kind(&self) -> MessagePatternKind {
466        self.kind
467    }
468
469    pub fn module_name(&self) -> Option<&str> {
470        self.module_name.as_deref()
471    }
472
473    pub fn with_pipe<P>(mut self, pipe: P) -> Self
474    where
475        P: TransportPipe,
476    {
477        self.pipes.push(Arc::new(pipe));
478        self
479    }
480
481    pub fn with_guard<G>(mut self, guard: G) -> Self
482    where
483        G: TransportGuard,
484    {
485        self.guards.push(Arc::new(guard));
486        self
487    }
488
489    pub fn with_execution_guard<G>(mut self, guard: G) -> Self
490    where
491        G: Guard,
492    {
493        self.guards
494            .push(Arc::new(ExecutionTransportGuard { inner: guard }));
495        self
496    }
497
498    pub(crate) fn with_execution_pipeline_prefix(
499        mut self,
500        guards: &[Arc<dyn Guard>],
501        interceptors: &[Arc<dyn ExecutionInterceptor>],
502    ) -> Self {
503        self.guards = prepend_execution_guards(guards, self.guards);
504        self.interceptors = prepend_execution_interceptors(interceptors, self.interceptors);
505        self
506    }
507
508    pub fn with_interceptor<I>(mut self, interceptor: I) -> Self
509    where
510        I: TransportInterceptor,
511    {
512        self.interceptors.push(Arc::new(interceptor));
513        self
514    }
515
516    pub fn with_execution_interceptor<I>(mut self, interceptor: I) -> Self
517    where
518        I: ExecutionInterceptor,
519    {
520        self.interceptors
521            .push(Arc::new(ExecutionTransportInterceptor {
522                inner: interceptor,
523            }));
524        self
525    }
526
527    pub fn with_payload_validation<T>(mut self) -> Self
528    where
529        T: DeserializeOwned + Validate + 'static,
530    {
531        self.validators.push(Arc::new(|message| {
532            message.validated_data::<T>().map(|_| ())
533        }));
534        self
535    }
536
537    pub async fn dispatch(&self, mut message: TransportMessage) -> Result<Option<TransportReply>> {
538        if message.pattern != self.pattern {
539            return Err(BootError::NotFound(format!(
540                "message pattern {}",
541                message.pattern
542            )));
543        }
544
545        let context = TransportContext::new(self, &message.pattern);
546        for guard in &self.guards {
547            let can_activate = guard.can_activate(context.clone()).await?;
548            if !can_activate {
549                return Err(BootError::Forbidden(format!(
550                    "message pattern {}",
551                    message.pattern
552                )));
553            }
554        }
555
556        for interceptor in &self.interceptors {
557            interceptor.before(context.clone()).await?;
558        }
559
560        for pipe in &self.pipes {
561            message = pipe.transform(message).await?;
562        }
563
564        for validator in &self.validators {
565            validator(&message)?;
566        }
567
568        let mut reply = self.handler.call(message).await?;
569        if self.kind == MessagePatternKind::Event {
570            reply = None;
571        }
572
573        for interceptor in self.interceptors.iter().rev() {
574            reply = interceptor.after(context.clone(), reply).await?;
575        }
576        Ok(reply)
577    }
578
579    pub(crate) fn with_module_name(mut self, module_name: &str) -> Self {
580        self.module_name = Some(module_name.to_string());
581        self
582    }
583}
584
585fn prepend_execution_guards(
586    prefix: &[Arc<dyn Guard>],
587    values: Vec<Arc<dyn TransportGuard>>,
588) -> Vec<Arc<dyn TransportGuard>> {
589    let mut merged = prefix
590        .iter()
591        .cloned()
592        .map(|guard| Arc::new(ExecutionTransportGuard { inner: guard }) as Arc<dyn TransportGuard>)
593        .collect::<Vec<_>>();
594    merged.extend(values);
595    merged
596}
597
598fn prepend_execution_interceptors(
599    prefix: &[Arc<dyn ExecutionInterceptor>],
600    values: Vec<Arc<dyn TransportInterceptor>>,
601) -> Vec<Arc<dyn TransportInterceptor>> {
602    let mut merged = prefix
603        .iter()
604        .cloned()
605        .map(|interceptor| {
606            Arc::new(ExecutionTransportInterceptor { inner: interceptor })
607                as Arc<dyn TransportInterceptor>
608        })
609        .collect::<Vec<_>>();
610    merged.extend(values);
611    merged
612}
613
614/// Adapter trait for message transports such as in-process, Redis, NATS, or Kafka.
615pub trait MessageTransport {
616    type Output;
617
618    fn build(&self, app: crate::BootApplication) -> Result<Self::Output>;
619
620    fn serve(&self, app: crate::BootApplication) -> BoxFuture<'static, Result<()>>;
621}
622
623/// In-process transport useful for tests and single-process message dispatch.
624#[derive(Debug, Clone, Copy, Default)]
625pub struct InProcessTransport;
626
627impl InProcessTransport {
628    pub fn new() -> Self {
629        Self
630    }
631}
632
633impl MessageTransport for InProcessTransport {
634    type Output = InProcessTransportClient;
635
636    fn build(&self, app: crate::BootApplication) -> Result<Self::Output> {
637        Ok(InProcessTransportClient { app })
638    }
639
640    fn serve(&self, _app: crate::BootApplication) -> BoxFuture<'static, Result<()>> {
641        Box::pin(async { Ok(()) })
642    }
643}
644
645/// In-process message client backed by a resolved Boot application.
646#[derive(Clone)]
647pub struct InProcessTransportClient {
648    app: crate::BootApplication,
649}
650
651impl InProcessTransportClient {
652    pub async fn send(&self, message: TransportMessage) -> Result<Option<TransportReply>> {
653        self.app.dispatch_message(message).await
654    }
655
656    pub async fn emit(&self, message: TransportMessage) -> Result<()> {
657        self.app.emit_message(message).await
658    }
659}
660
661fn validate_pattern(pattern: &str) -> Result<()> {
662    if pattern.trim().is_empty() {
663        return Err(BootError::BadRequest(
664            "message pattern cannot be empty".to_string(),
665        ));
666    }
667    Ok(())
668}