Skip to main content

lores_app_node/stores/
mod.rs

1use std::future::Future;
2use std::pin::Pin;
3
4use futures::Stream;
5
6use crate::types::{NodeId, OperationId};
7
8/// Result returned by [`OperationStore::publish`].
9pub(crate) struct StorePublishResult {
10    /// p2panda operation hash, if the backend can provide it synchronously.
11    pub operation_id: Option<OperationId>,
12    /// Identity of the node that persisted the operation, if known.
13    pub node_id: Option<NodeId>,
14}
15
16/// Error returned when publishing, subscribing to, or replaying operations.
17#[derive(Debug)]
18pub enum StoreError {
19    /// No region has been bound to the given app/instance on the server.
20    RegionNotBound(String),
21    /// Any other error.
22    Other(String),
23}
24
25impl std::fmt::Display for StoreError {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        match self {
28            StoreError::RegionNotBound(msg) => write!(f, "{msg}"),
29            StoreError::Other(msg) => write!(f, "{msg}"),
30        }
31    }
32}
33
34impl std::error::Error for StoreError {}
35
36/// Metadata forwarded from the p2panda layer alongside a raw payload.
37/// Fields are `None` for locally-originated operations (pre-network assignment).
38pub(crate) struct RawOperationEvent {
39    pub payload: Vec<u8>,
40    /// 32-byte p2panda author public key.
41    pub author: Option<Vec<u8>>,
42    /// 32-byte p2panda operation hash.
43    pub operation_id: Option<Vec<u8>>,
44    /// Unix timestamp in milliseconds.
45    pub timestamp: Option<u64>,
46}
47
48impl RawOperationEvent {
49    /// Construct an event for a locally-published operation with no p2panda metadata.
50    pub(crate) fn new_local(payload: Vec<u8>) -> Self {
51        Self {
52            payload,
53            author: None,
54            operation_id: None,
55            timestamp: None,
56        }
57    }
58}
59
60/// A boxed, heap-allocated stream of raw operation events.
61pub(crate) type OperationStream = Pin<Box<dyn Stream<Item = Result<RawOperationEvent, StoreError>> + Send>>;
62
63/// Internal trait over raw-bytes operation delivery.
64///
65/// App developers never interact with this directly — they use [`crate::AppNode`]
66/// and its named constructors (`grpc`, etc.).
67pub(crate) trait OperationStore: Send + Sync + 'static {
68    /// Returns operation metadata if the backend can provide it synchronously.
69    fn publish(
70        &mut self,
71        payload: Vec<u8>,
72        idempotency_key: Option<String>,
73    ) -> Pin<Box<dyn Future<Output = Result<StorePublishResult, StoreError>> + Send + '_>>;
74
75    /// Open a subscription to incoming operations.
76    ///
77    /// The outer `Result` covers connection-time errors (e.g. `RegionNotBound`).
78    /// The inner stream yields individual operation payloads or per-item errors.
79    fn subscribe(&mut self) -> Pin<Box<dyn Future<Output = Result<OperationStream, StoreError>> + Send + '_>>;
80
81    /// Replay all operations in insertion order.
82    fn replay(&mut self) -> Pin<Box<dyn Future<Output = Result<OperationStream, StoreError>> + Send + '_>> {
83        Box::pin(async move {
84            let s: OperationStream = Box::pin(futures::stream::empty());
85            Ok(s)
86        })
87    }
88}
89pub(crate) mod grpc;
90pub(crate) mod local;
91pub(crate) mod outbox;