iroh_docs/
protocol.rs

1//! [`ProtocolHandler`] implementation for the docs [`Engine`].
2
3use std::{path::PathBuf, sync::Arc};
4
5use anyhow::Result;
6use futures_lite::future::Boxed as BoxedFuture;
7use iroh::{endpoint::Connection, protocol::ProtocolHandler};
8use iroh_blobs::net_protocol::{Blobs, ProtectCb};
9use iroh_gossip::net::Gossip;
10
11use crate::{
12    engine::{DefaultAuthorStorage, Engine},
13    store::Store,
14};
15
16impl<S: iroh_blobs::store::Store> ProtocolHandler for Docs<S> {
17    fn accept(&self, conn: Connection) -> BoxedFuture<Result<()>> {
18        let this = self.engine.clone();
19        Box::pin(async move { this.handle_connection(conn).await })
20    }
21
22    fn shutdown(&self) -> BoxedFuture<()> {
23        let this = self.engine.clone();
24        Box::pin(async move {
25            if let Err(err) = this.shutdown().await {
26                tracing::warn!("shutdown error: {:?}", err);
27            }
28        })
29    }
30}
31
32/// Docs protocol.
33#[derive(Debug, Clone)]
34pub struct Docs<S> {
35    engine: Arc<Engine<S>>,
36    #[cfg(feature = "rpc")]
37    pub(crate) rpc_handler: Arc<std::sync::OnceLock<crate::rpc::RpcHandler>>,
38}
39
40impl Docs<()> {
41    /// Create a new [`Builder`] for the docs protocol, using in memory replica and author storage.
42    pub fn memory() -> Builder {
43        Builder::default()
44    }
45
46    /// Create a new [`Builder`] for the docs protocol, using a persistent replica and author storage
47    /// in the given directory.
48    pub fn persistent(path: PathBuf) -> Builder {
49        Builder { path: Some(path) }
50    }
51}
52
53impl<S: iroh_blobs::store::Store> Docs<S> {
54    /// Get an in memory client to interact with the docs engine.
55    #[cfg(feature = "rpc")]
56    pub fn client(&self) -> &crate::rpc::client::docs::MemClient {
57        &self
58            .rpc_handler
59            .get_or_init(|| crate::rpc::RpcHandler::new(self.engine.clone()))
60            .client
61    }
62
63    /// Create a new docs protocol with the given engine.
64    ///
65    /// Note that usually you would use the [`Builder`] to create a new docs protocol.
66    pub fn new(engine: Engine<S>) -> Self {
67        Self {
68            engine: Arc::new(engine),
69            #[cfg(feature = "rpc")]
70            rpc_handler: Default::default(),
71        }
72    }
73
74    /// Handle a docs request from the RPC server.
75    #[cfg(feature = "rpc")]
76    pub async fn handle_rpc_request<
77        C: quic_rpc::server::ChannelTypes<crate::rpc::proto::RpcService>,
78    >(
79        self,
80        msg: crate::rpc::proto::Request,
81        chan: quic_rpc::server::RpcChannel<crate::rpc::proto::RpcService, C>,
82    ) -> Result<(), quic_rpc::server::RpcServerError<C>> {
83        crate::rpc::Handler(self.engine.clone())
84            .handle_rpc_request(msg, chan)
85            .await
86    }
87
88    /// Get the protect callback for the docs engine.
89    pub fn protect_cb(&self) -> ProtectCb {
90        self.engine.protect_cb()
91    }
92}
93
94/// Builder for the docs protocol.
95#[derive(Debug, Default)]
96pub struct Builder {
97    path: Option<PathBuf>,
98}
99
100impl Builder {
101    /// Build a [`Docs`] protocol given a [`Blobs`] and [`Gossip`] protocol.
102    pub async fn spawn<S: iroh_blobs::store::Store>(
103        self,
104        blobs: &Blobs<S>,
105        gossip: &Gossip,
106    ) -> anyhow::Result<Docs<S>> {
107        let replica_store = match self.path {
108            Some(ref path) => Store::persistent(path.join("docs.redb"))?,
109            None => Store::memory(),
110        };
111        let author_store = match self.path {
112            Some(ref path) => DefaultAuthorStorage::Persistent(path.join("default-author")),
113            None => DefaultAuthorStorage::Mem,
114        };
115        let engine = Engine::spawn(
116            blobs.endpoint().clone(),
117            gossip.clone(),
118            replica_store,
119            blobs.store().clone(),
120            blobs.downloader().clone(),
121            author_store,
122            blobs.rt().clone(),
123        )
124        .await?;
125        Ok(Docs::new(engine))
126    }
127}