1use 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#[derive(Debug, Clone)]
18pub struct Docs {
19 engine: Arc<Engine>,
20 api: DocsApi,
21}
22
23impl Docs {
24 pub fn memory() -> Builder {
26 Builder::default()
27 }
28
29 pub fn persistent(path: PathBuf) -> Builder {
32 Builder {
33 path: Some(path),
34 protect_cb: None,
35 }
36 }
37
38 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 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#[derive(Debug, Default)]
77pub struct Builder {
78 path: Option<PathBuf>,
79 protect_cb: Option<ProtectCallbackHandler>,
80}
81
82impl Builder {
83 pub fn protect_handler(mut self, protect_handler: ProtectCallbackHandler) -> Self {
87 self.protect_cb = Some(protect_handler);
88 self
89 }
90
91 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}