photon_backend/backend/
generic.rs1use std::pin::Pin;
4use std::sync::Arc;
5
6use async_trait::async_trait;
7use futures::stream::Stream;
8use serde_json::Value;
9
10use super::capabilities::BackendCapabilities;
11use super::context::BackendContext;
12use super::photon_backend::PhotonBackend;
13use crate::error::Result;
14use crate::models::Event;
15use crate::publish_routing::resolve_publish_target;
16use crate::registry::TopicRegistry;
17use crate::storage::{InProcStoragePort, StoragePort};
18
19pub struct GenericPhotonBackend {
21 port: Arc<dyn StoragePort>,
22 registry: TopicRegistry,
23 capabilities: BackendCapabilities,
24}
25
26impl GenericPhotonBackend {
27 #[must_use]
29 pub fn new(port: Arc<dyn StoragePort>, registry: TopicRegistry) -> Self {
30 let capabilities = port.capabilities();
31 Self {
32 port,
33 registry,
34 capabilities,
35 }
36 }
37
38 #[must_use]
40 pub const fn capabilities(&self) -> BackendCapabilities {
41 self.capabilities
42 }
43
44 pub fn install_mem(ctx: BackendContext) -> Result<Arc<dyn PhotonBackend>> {
50 let port = Arc::new(InProcStoragePort::new(
51 crate::event::TransportCrypto::from_env()?,
52 ));
53 Ok(Arc::new(Self::new(port, ctx.registry)))
54 }
55
56 pub fn install_with_port(
62 ctx: BackendContext,
63 port: Arc<dyn StoragePort>,
64 ) -> Result<Arc<dyn PhotonBackend>> {
65 Ok(Arc::new(Self::new(port, ctx.registry)))
66 }
67}
68
69#[async_trait]
70impl PhotonBackend for GenericPhotonBackend {
71 fn telemetry_label(&self) -> &'static str {
72 self.capabilities.telemetry_label
73 }
74
75 fn capabilities(&self) -> BackendCapabilities {
76 self.capabilities
77 }
78
79 async fn publish(
80 &self,
81 topic_name: &str,
82 topic_key: Option<&str>,
83 actor_json: Value,
84 payload_json: Value,
85 ) -> Result<String> {
86 let target = resolve_publish_target(
87 &self.registry,
88 topic_name,
89 topic_key,
90 &payload_json,
91 );
92 let event = self
93 .port
94 .append(
95 topic_name,
96 target.topic_key.as_deref(),
97 actor_json,
98 payload_json,
99 )
100 .await?;
101 Ok(event.event_id)
102 }
103
104 fn subscribe(
105 &self,
106 topic_name: String,
107 topic_key_filter: Option<String>,
108 after_seq: Option<i64>,
109 ) -> Pin<Box<dyn Stream<Item = Result<Event>> + Send>> {
110 self.port
111 .subscribe(topic_name, topic_key_filter, after_seq)
112 }
113
114 async fn get_event(&self, event_id: &str) -> Result<Option<Event>> {
115 if !self.capabilities.supports_get_event {
116 return Ok(None);
117 }
118 self.port.get_event(event_id).await
119 }
120
121 fn registry(&self) -> &TopicRegistry {
122 &self.registry
123 }
124
125 async fn get_checkpoint_seq(
126 &self,
127 subscription_name: &str,
128 topic_name: &str,
129 topic_key: Option<&str>,
130 ) -> Result<Option<i64>> {
131 self.port
132 .load_checkpoint(subscription_name, topic_name, topic_key)
133 .await
134 }
135
136 async fn set_checkpoint(
137 &self,
138 subscription_name: &str,
139 topic_name: &str,
140 topic_key: Option<&str>,
141 last_seq: i64,
142 ) -> Result<()> {
143 self.port
144 .commit_checkpoint(subscription_name, topic_name, topic_key, last_seq)
145 .await
146 }
147}