use std::{path::PathBuf, sync::Arc};
use anyhow::Result;
use iroh::{endpoint::Connection, protocol::ProtocolHandler, Endpoint};
use iroh_blobs::api::Store as BlobsStore;
use iroh_gossip::net::Gossip;
use crate::{
api::DocsApi,
engine::{DefaultAuthorStorage, Engine, ProtectCallbackHandler},
store::Store,
};
#[derive(Debug, Clone)]
pub struct Docs {
engine: Arc<Engine>,
api: DocsApi,
}
impl Docs {
pub fn memory() -> Builder {
Builder::default()
}
pub fn persistent(path: PathBuf) -> Builder {
Builder {
path: Some(path),
protect_cb: None,
}
}
pub fn new(engine: Engine) -> Self {
let engine = Arc::new(engine);
let api = DocsApi::spawn(engine.clone());
Self { engine, api }
}
pub fn api(&self) -> &DocsApi {
&self.api
}
}
impl std::ops::Deref for Docs {
type Target = DocsApi;
fn deref(&self) -> &Self::Target {
&self.api
}
}
impl ProtocolHandler for Docs {
async fn accept(&self, connection: Connection) -> Result<(), iroh::protocol::AcceptError> {
self.engine
.handle_connection(connection)
.await
.map_err(|err| err.into_boxed_dyn_error())?;
Ok(())
}
async fn shutdown(&self) {
if let Err(err) = self.engine.shutdown().await {
tracing::warn!("shutdown error: {:?}", err);
}
}
}
#[derive(Debug, Default)]
pub struct Builder {
path: Option<PathBuf>,
protect_cb: Option<ProtectCallbackHandler>,
}
impl Builder {
pub fn protect_handler(mut self, protect_handler: ProtectCallbackHandler) -> Self {
self.protect_cb = Some(protect_handler);
self
}
pub async fn spawn(
self,
endpoint: Endpoint,
blobs: BlobsStore,
gossip: Gossip,
) -> anyhow::Result<Docs> {
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 downloader = blobs.downloader(&endpoint);
let engine = Engine::spawn(
endpoint,
gossip,
replica_store,
blobs,
downloader,
author_store,
self.protect_cb,
)
.await?;
Ok(Docs::new(engine))
}
}