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}