|
1 | 1 | //! [`ProtocolHandler`] implementation for the docs [`Engine`]. |
2 | 2 |
|
| 3 | +use std::{path::PathBuf, sync::Arc}; |
| 4 | + |
3 | 5 | use anyhow::Result; |
4 | 6 | use futures_lite::future::Boxed as BoxedFuture; |
5 | 7 | use iroh::{endpoint::Connecting, protocol::ProtocolHandler}; |
| 8 | +use iroh_blobs::net_protocol::{Blobs, ProtectCb}; |
| 9 | +use iroh_gossip::net::Gossip; |
6 | 10 |
|
7 | | -use crate::engine::Engine; |
| 11 | +use crate::{ |
| 12 | + engine::{DefaultAuthorStorage, Engine}, |
| 13 | + store::Store, |
| 14 | +}; |
8 | 15 |
|
9 | | -impl<D: iroh_blobs::store::Store> ProtocolHandler for Engine<D> { |
| 16 | +impl<S: iroh_blobs::store::Store> ProtocolHandler for Docs<S> { |
10 | 17 | fn accept(&self, conn: Connecting) -> BoxedFuture<Result<()>> { |
11 | | - let this = self.clone(); |
| 18 | + let this = self.engine.clone(); |
12 | 19 | Box::pin(async move { this.handle_connection(conn).await }) |
13 | 20 | } |
14 | 21 |
|
15 | 22 | fn shutdown(&self) -> BoxedFuture<()> { |
16 | | - let this = self.clone(); |
| 23 | + let this = self.engine.clone(); |
17 | 24 | Box::pin(async move { |
18 | 25 | if let Err(err) = this.shutdown().await { |
19 | 26 | tracing::warn!("shutdown error: {:?}", err); |
20 | 27 | } |
21 | 28 | }) |
22 | 29 | } |
23 | 30 | } |
| 31 | + |
| 32 | +/// Docs protocol. |
| 33 | +#[derive(Debug, Clone)] |
| 34 | +pub 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 | + |
| 40 | +impl 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 | + |
| 53 | +impl<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)] |
| 96 | +pub struct Builder { |
| 97 | + path: Option<PathBuf>, |
| 98 | +} |
| 99 | + |
| 100 | +impl 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 | +} |
0 commit comments