use std::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(Default, Debug)]
enum Storage {
#[default]
Memory,
#[cfg(feature = "fs-store")]
Persistent(std::path::PathBuf),
}
#[derive(Debug, Clone)]
pub struct Docs {
engine: Arc<Engine>,
api: DocsApi,
}
impl Docs {
pub fn memory() -> Builder {
Builder::default()
}
#[cfg(feature = "fs-store")]
pub fn persistent(path: std::path::PathBuf) -> Builder {
Builder {
storage: Storage::Persistent(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| iroh::protocol::AcceptError::from_err(n0_error::anyerr!(err)))?;
Ok(())
}
async fn shutdown(&self) {
if let Err(err) = self.engine.shutdown().await {
tracing::warn!("shutdown error: {:?}", err);
}
}
}
#[derive(Debug, Default)]
pub struct Builder {
storage: Storage,
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.storage {
Storage::Memory => Store::memory(),
#[cfg(feature = "fs-store")]
Storage::Persistent(path) => Store::persistent(path.join("docs.redb"))?,
};
let author_store = match &self.storage {
Storage::Memory => DefaultAuthorStorage::Mem,
#[cfg(feature = "fs-store")]
Storage::Persistent(path) => {
DefaultAuthorStorage::Persistent(path.join("default-author"))
}
};
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))
}
}