iroh_docs/
protocol.rs

1//! [`ProtocolHandler`] implementation for the docs [`Engine`].
2
3use std::{path::PathBuf, sync::Arc};
4
5use anyhow::Result;
6use iroh::{endpoint::Connection, protocol::ProtocolHandler, Endpoint};
7use iroh_blobs::api::Store as BlobsStore;
8use iroh_gossip::net::Gossip;
9
10use crate::{
11    api::DocsApi,
12    engine::{DefaultAuthorStorage, Engine, ProtectCallbackHandler},
13    store::Store,
14};
15
16/// Docs protocol.
17#[derive(Debug, Clone)]
18pub struct Docs {
19    engine: Arc<Engine>,
20    api: DocsApi,
21}
22
23impl Docs {
24    /// Create a new [`Builder`] for the docs protocol, using in memory replica and author storage.
25    pub fn memory() -> Builder {
26        Builder::default()
27    }
28
29    /// Create a new [`Builder`] for the docs protocol, using a persistent replica and author storage
30    /// in the given directory.
31    pub fn persistent(path: PathBuf) -> Builder {
32        Builder {
33            path: Some(path),
34            protect_cb: None,
35        }
36    }
37
38    /// Creates a new [`Docs`] from an [`Engine`].
39    pub fn new(engine: Engine) -> Self {
40        let engine = Arc::new(engine);
41        let api = DocsApi::spawn(engine.clone());
42        Self { engine, api }
43    }
44
45    /// Returns the API for this docs instance.
46    pub fn api(&self) -> &DocsApi {
47        &self.api
48    }
49}
50
51impl std::ops::Deref for Docs {
52    type Target = DocsApi;
53
54    fn deref(&self) -> &Self::Target {
55        &self.api
56    }
57}
58
59impl ProtocolHandler for Docs {
60    async fn accept(&self, connection: Connection) -> Result<(), iroh::protocol::AcceptError> {
61        self.engine
62            .handle_connection(connection)
63            .await
64            .map_err(|err| err.into_boxed_dyn_error())?;
65        Ok(())
66    }
67
68    async fn shutdown(&self) {
69        if let Err(err) = self.engine.shutdown().await {
70            tracing::warn!("shutdown error: {:?}", err);
71        }
72    }
73}
74
75/// Builder for the docs protocol.
76#[derive(Debug, Default)]
77pub struct Builder {
78    path: Option<PathBuf>,
79    protect_cb: Option<ProtectCallbackHandler>,
80}
81
82impl Builder {
83    /// Set the garbage collection protection handler for blobs.
84    ///
85    /// See [`ProtectCallbackHandler::new`] for details.
86    pub fn protect_handler(mut self, protect_handler: ProtectCallbackHandler) -> Self {
87        self.protect_cb = Some(protect_handler);
88        self
89    }
90
91    /// Build a [`Docs`] protocol given a [`BlobsStore`] and [`Gossip`] protocol.
92    pub async fn spawn(
93        self,
94        endpoint: Endpoint,
95        blobs: BlobsStore,
96        gossip: Gossip,
97    ) -> anyhow::Result<Docs> {
98        let replica_store = match self.path {
99            Some(ref path) => Store::persistent(path.join("docs.redb"))?,
100            None => Store::memory(),
101        };
102        let author_store = match self.path {
103            Some(ref path) => DefaultAuthorStorage::Persistent(path.join("default-author")),
104            None => DefaultAuthorStorage::Mem,
105        };
106        let downloader = blobs.downloader(&endpoint);
107        let engine = Engine::spawn(
108            endpoint,
109            gossip,
110            replica_store,
111            blobs,
112            downloader,
113            author_store,
114            self.protect_cb,
115        )
116        .await?;
117        Ok(Docs::new(engine))
118    }
119}