p2panda_sync/
traits.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Interfaces for implementing sync protocols and managers.
4use std::error::Error as StdError;
5use std::fmt::Debug;
6use std::pin::Pin;
7
8use futures::Sink;
9use futures_util::Stream;
10use serde::{Deserialize, Serialize};
11
12use crate::{FromSync, SessionConfig, ToSync};
13
14/// Generic protocol interface which runs over a sink and stream pair.
15pub trait Protocol {
16    type Output;
17    type Error: StdError + Send + Sync + 'static;
18    type Message: Serialize + for<'a> Deserialize<'a>;
19
20    fn run(
21        self,
22        sink: &mut (impl Sink<Self::Message, Error = impl Debug> + Unpin),
23        stream: &mut (impl Stream<Item = Result<Self::Message, impl Debug>> + Unpin),
24    ) -> impl Future<Output = Result<Self::Output, Self::Error>>;
25}
26
27/// Interface for managing sync sessions and consuming events they emit.
28#[allow(clippy::type_complexity)]
29pub trait Manager<T> {
30    type Protocol: Protocol + Send + 'static;
31    type Args: Clone + Send + 'static;
32    type Message: Clone + Send + 'static;
33    type Event: Clone + Debug + Send + 'static;
34    type Error: StdError + Send + Sync + 'static;
35
36    fn from_args(args: Self::Args) -> Self;
37
38    /// Instantiate a new sync session.
39    fn session(
40        &mut self,
41        session_id: u64,
42        config: &SessionConfig<T>,
43    ) -> impl Future<Output = Self::Protocol>;
44
45    /// Retrieve a send handle to an already existing sync session.
46    fn session_handle(
47        &self,
48        session_id: u64,
49    ) -> impl Future<Output = Option<Pin<Box<dyn Sink<ToSync<Self::Message>, Error = Self::Error>>>>>;
50
51    /// Subscribe to the manager event stream.
52    fn subscribe(&mut self) -> impl Stream<Item = FromSync<Self::Event>> + Send + Unpin + 'static;
53}
54
55/// Maps a topic to a user defined data type being sent over the wire during sync.
56///
57/// It defines the type of data it is expecting to sync and how the scope for a particular session
58/// should be identified; users provide an implementation of the `TopicMap` trait in order to
59/// define how this mapping occurs.
60///
61/// Since `TopicMap` is generic we can use the same mapping across different sync implementations
62/// for the same data type when necessary.
63///
64/// For example a `TopicMap` map implementation could map a generic `T` to a set of logs.
65///
66/// ## Designing `TopicMap` for applications
67///
68/// Considering an example chat application which is based on append-only log data types, we
69/// probably want to organise messages from an author for a certain chat group into one log each.
70/// Like this, a chat group can be expressed as a collection of one to potentially many logs (one
71/// per member of the group):
72///
73/// ```text
74/// All authors: A, B and C
75/// All chat groups: 1 and 2
76///
77/// "Chat group 1 with members A and B"
78/// - Log A1
79/// - Log B1
80///
81/// "Chat group 2 with members A, B and C"
82/// - Log A2
83/// - Log B2
84/// - Log C2
85/// ```
86///
87/// If we implement `T` to express that we're interested in syncing over a specific chat group,
88/// for example "Chat Group 2" we would implement `TopicMap` to give us all append-only logs of
89/// all members inside this group, that is the entries inside logs `A2`, `B2` and `C2`.
90pub trait TopicMap<T, V>: Clone {
91    type Error: StdError;
92
93    fn get(&self, topic: &T) -> impl Future<Output = Result<V, Self::Error>>;
94}