Skip to main content

photon_backend/backend/
photon_backend.rs

1//! Object-safe backend trait for Photon publish/subscribe runtimes.
2//!
3//! Prefer wiring a [`crate::storage::StoragePort`] through
4//! [`PhotonBuilder::storage_port`](https://docs.rs/uf-photon/latest/photon/struct.PhotonBuilder.html#method.storage_port).
5//! Implement this trait only for custom delivery stacks, then install via
6//! [`PhotonBuilder::backend_with_context`](https://docs.rs/uf-photon/latest/photon/struct.PhotonBuilder.html#method.backend_with_context).
7//!
8//! See also: [`crate::storage::StoragePort`], [`crate::GenericPhotonBackend`].
9
10use std::pin::Pin;
11
12use async_trait::async_trait;
13use futures::stream::Stream;
14use serde_json::Value;
15
16use crate::backend::BackendCapabilities;
17use crate::error::Result;
18use crate::models::Event;
19use crate::registry::TopicRegistry;
20
21/// Backend for publish/subscribe delivery and checkpoint persistence.
22///
23/// Most hosts never implement this — use a storage adapter and the default
24/// [`GenericPhotonBackend`](crate::GenericPhotonBackend). Custom delivery example:
25///
26/// ```rust,ignore
27/// use std::sync::Arc;
28/// use photon_backend::{BackendContext, PhotonBackend};
29/// use photon_runtime::Photon;
30///
31/// let _photon = Photon::builder()
32///     .backend_with_context(|ctx: BackendContext| {
33///         // return Ok(Arc::new(MyBackend::new(ctx)) as Arc<dyn PhotonBackend>)
34///         photon_backend::EmbeddedBackend::install_mem(ctx)
35///     })
36///     .auto_registry()
37///     .build()?;
38/// ```
39#[async_trait]
40pub trait PhotonBackend: Send + Sync {
41    /// Stable telemetry label for ops metrics (e.g. `"mem"`, `"nats"`).
42    fn telemetry_label(&self) -> &'static str {
43        "custom"
44    }
45
46    /// Adapter capabilities (replay window, get-by-id support, …).
47    fn capabilities(&self) -> BackendCapabilities {
48        BackendCapabilities::mem()
49    }
50
51    /// Append an event and return its event id.
52    ///
53    /// # Contract
54    ///
55    /// - Returns the stable `event_id` assigned by the underlying storage port.
56    /// - Ordering and dedupe are per `(topic_name, topic_key)` partition, not global.
57    async fn publish(
58        &self,
59        topic_name: &str,
60        topic_key: Option<&str>,
61        actor_json: Value,
62        payload_json: Value,
63    ) -> Result<String>;
64
65    /// Stream events for a topic partition, optionally replaying after `after_seq`.
66    ///
67    /// # Contract
68    ///
69    /// - Same semantics as [`crate::storage::StoragePort::subscribe`].
70    fn subscribe(
71        &self,
72        topic_name: String,
73        topic_key_filter: Option<String>,
74        after_seq: Option<i64>,
75    ) -> Pin<Box<dyn Stream<Item = Result<Event>> + Send>>;
76
77    /// Load a single event by id.
78    ///
79    /// # Contract
80    ///
81    /// - Returns `None` when unknown or truncated; see backend [`BackendCapabilities`].
82    async fn get_event(&self, event_id: &str) -> Result<Option<Event>>;
83
84    /// Inventory-discovered topic descriptors.
85    fn registry(&self) -> &TopicRegistry;
86
87    /// Load the last committed checkpoint seq for a subscription partition.
88    ///
89    /// # Contract
90    ///
91    /// - Returns `None` when no checkpoint exists.
92    async fn get_checkpoint_seq(
93        &self,
94        subscription_name: &str,
95        topic_name: &str,
96        topic_key: Option<&str>,
97    ) -> Result<Option<i64>>;
98
99    /// Persist the high-water checkpoint seq for a subscription partition.
100    ///
101    /// # Contract
102    ///
103    /// - `last_seq` must not regress; coalesced writes may batch behind the scenes.
104    async fn set_checkpoint(
105        &self,
106        subscription_name: &str,
107        topic_name: &str,
108        topic_key: Option<&str>,
109        last_seq: i64,
110    ) -> Result<()>;
111}