use std::{path::PathBuf, sync::Arc};
use anyhow::Result;
use futures_lite::future::Boxed as BoxedFuture;
use iroh::{endpoint::Connection, protocol::ProtocolHandler};
use iroh_blobs::net_protocol::{Blobs, ProtectCb};
use iroh_gossip::net::Gossip;
use crate::{
engine::{DefaultAuthorStorage, Engine},
store::Store,
};
impl<S: iroh_blobs::store::Store> ProtocolHandler for Docs<S> {
fn accept(&self, conn: Connection) -> BoxedFuture<Result<()>> {
let this = self.engine.clone();
Box::pin(async move { this.handle_connection(conn).await })
}
fn shutdown(&self) -> BoxedFuture<()> {
let this = self.engine.clone();
Box::pin(async move {
if let Err(err) = this.shutdown().await {
tracing::warn!("shutdown error: {:?}", err);
}
})
}
}
#[derive(Debug, Clone)]
pub struct Docs<S> {
engine: Arc<Engine<S>>,
#[cfg(feature = "rpc")]
pub(crate) rpc_handler: Arc<std::sync::OnceLock<crate::rpc::RpcHandler>>,
}
impl Docs<()> {
pub fn memory() -> Builder {
Builder::default()
}
pub fn persistent(path: PathBuf) -> Builder {
Builder { path: Some(path) }
}
}
impl<S: iroh_blobs::store::Store> Docs<S> {
#[cfg(feature = "rpc")]
pub fn client(&self) -> &crate::rpc::client::docs::MemClient {
&self
.rpc_handler
.get_or_init(|| crate::rpc::RpcHandler::new(self.engine.clone()))
.client
}
pub fn new(engine: Engine<S>) -> Self {
Self {
engine: Arc::new(engine),
#[cfg(feature = "rpc")]
rpc_handler: Default::default(),
}
}
#[cfg(feature = "rpc")]
pub async fn handle_rpc_request<
C: quic_rpc::server::ChannelTypes<crate::rpc::proto::RpcService>,
>(
self,
msg: crate::rpc::proto::Request,
chan: quic_rpc::server::RpcChannel<crate::rpc::proto::RpcService, C>,
) -> Result<(), quic_rpc::server::RpcServerError<C>> {
crate::rpc::Handler(self.engine.clone())
.handle_rpc_request(msg, chan)
.await
}
pub fn protect_cb(&self) -> ProtectCb {
self.engine.protect_cb()
}
}
#[derive(Debug, Default)]
pub struct Builder {
path: Option<PathBuf>,
}
impl Builder {
pub async fn spawn<S: iroh_blobs::store::Store>(
self,
blobs: &Blobs<S>,
gossip: &Gossip,
) -> anyhow::Result<Docs<S>> {
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 engine = Engine::spawn(
blobs.endpoint().clone(),
gossip.clone(),
replica_store,
blobs.store().clone(),
blobs.downloader().clone(),
author_store,
blobs.rt().clone(),
)
.await?;
Ok(Docs::new(engine))
}
}