1use 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#[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 pub fn memory() -> Builder {
43 Builder::default()
44 }
45
46 pub fn persistent(path: PathBuf) -> Builder {
49 Builder { path: Some(path) }
50 }
51}
52
53impl<S: iroh_blobs::store::Store> Docs<S> {
54 #[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 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 #[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 pub fn protect_cb(&self) -> ProtectCb {
90 self.engine.protect_cb()
91 }
92}
93
94#[derive(Debug, Default)]
96pub struct Builder {
97 path: Option<PathBuf>,
98}
99
100impl Builder {
101 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}