iroh_docs/
protocol.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! [`ProtocolHandler`] implementation for the docs [`Engine`].

use std::{path::PathBuf, sync::Arc};

use anyhow::Result;
use futures_lite::future::Boxed as BoxedFuture;
use iroh::{endpoint::Connection, protocol::ProtocolHandler};
use iroh_blobs::net_protocol::{Blobs, ProtectCb};
use iroh_gossip::net::Gossip;

use crate::{
    engine::{DefaultAuthorStorage, Engine},
    store::Store,
};

impl<S: iroh_blobs::store::Store> ProtocolHandler for Docs<S> {
    fn accept(&self, conn: Connection) -> BoxedFuture<Result<()>> {
        let this = self.engine.clone();
        Box::pin(async move { this.handle_connection(conn).await })
    }

    fn shutdown(&self) -> BoxedFuture<()> {
        let this = self.engine.clone();
        Box::pin(async move {
            if let Err(err) = this.shutdown().await {
                tracing::warn!("shutdown error: {:?}", err);
            }
        })
    }
}

/// Docs protocol.
#[derive(Debug, Clone)]
pub struct Docs<S> {
    engine: Arc<Engine<S>>,
    #[cfg(feature = "rpc")]
    pub(crate) rpc_handler: Arc<std::sync::OnceLock<crate::rpc::RpcHandler>>,
}

impl Docs<()> {
    /// Create a new [`Builder`] for the docs protocol, using in memory replica and author storage.
    pub fn memory() -> Builder {
        Builder::default()
    }

    /// Create a new [`Builder`] for the docs protocol, using a persistent replica and author storage
    /// in the given directory.
    pub fn persistent(path: PathBuf) -> Builder {
        Builder { path: Some(path) }
    }
}

impl<S: iroh_blobs::store::Store> Docs<S> {
    /// Get an in memory client to interact with the docs engine.
    #[cfg(feature = "rpc")]
    pub fn client(&self) -> &crate::rpc::client::docs::MemClient {
        &self
            .rpc_handler
            .get_or_init(|| crate::rpc::RpcHandler::new(self.engine.clone()))
            .client
    }

    /// Create a new docs protocol with the given engine.
    ///
    /// Note that usually you would use the [`Builder`] to create a new docs protocol.
    pub fn new(engine: Engine<S>) -> Self {
        Self {
            engine: Arc::new(engine),
            #[cfg(feature = "rpc")]
            rpc_handler: Default::default(),
        }
    }

    /// Handle a docs request from the RPC server.
    #[cfg(feature = "rpc")]
    pub async fn handle_rpc_request<
        C: quic_rpc::server::ChannelTypes<crate::rpc::proto::RpcService>,
    >(
        self,
        msg: crate::rpc::proto::Request,
        chan: quic_rpc::server::RpcChannel<crate::rpc::proto::RpcService, C>,
    ) -> Result<(), quic_rpc::server::RpcServerError<C>> {
        crate::rpc::Handler(self.engine.clone())
            .handle_rpc_request(msg, chan)
            .await
    }

    /// Get the protect callback for the docs engine.
    pub fn protect_cb(&self) -> ProtectCb {
        self.engine.protect_cb()
    }
}

/// Builder for the docs protocol.
#[derive(Debug, Default)]
pub struct Builder {
    path: Option<PathBuf>,
}

impl Builder {
    /// Build a [`Docs`] protocol given a [`Blobs`] and [`Gossip`] protocol.
    pub async fn spawn<S: iroh_blobs::store::Store>(
        self,
        blobs: &Blobs<S>,
        gossip: &Gossip,
    ) -> anyhow::Result<Docs<S>> {
        let replica_store = match self.path {
            Some(ref path) => Store::persistent(path.join("docs.redb"))?,
            None => Store::memory(),
        };
        let author_store = match self.path {
            Some(ref path) => DefaultAuthorStorage::Persistent(path.join("default-author")),
            None => DefaultAuthorStorage::Mem,
        };
        let engine = Engine::spawn(
            blobs.endpoint().clone(),
            gossip.clone(),
            replica_store,
            blobs.store().clone(),
            blobs.downloader().clone(),
            author_store,
            blobs.rt().clone(),
        )
        .await?;
        Ok(Docs::new(engine))
    }
}