use crate::guardian::error::{GuardianError, Result};
use bytes::Bytes;
use iroh_docs::{AuthorId, NamespaceId, api::Doc, protocol::Docs, store::Query, sync::Entry};
use std::sync::Arc;
use tracing::{debug, error, info, warn};
use super::IrohBackend;
#[derive(Clone)]
pub struct WillowDocs {
docs: Arc<Docs>,
default_author: Option<AuthorId>,
}
impl WillowDocs {
pub async fn new(backend: Arc<IrohBackend>) -> Result<Self> {
let docs_lock_guard = backend.get_docs().await?;
let docs_lock = docs_lock_guard.read().await;
let docs = docs_lock
.as_ref()
.ok_or_else(|| GuardianError::Other("Docs não inicializado no backend".into()))?
.clone();
drop(docs_lock);
Ok(Self {
docs: Arc::new(docs),
default_author: None,
})
}
pub async fn init_default_author(&mut self) -> Result<AuthorId> {
match self.docs.author_default().await {
Ok(author_id) => {
info!("Using existing default author: {:?}", author_id);
self.default_author = Some(author_id);
Ok(author_id)
}
Err(_) => {
match self.docs.author_create().await {
Ok(author_id) => {
info!("Created new default author: {:?}", author_id);
if let Err(e) = self.docs.author_set_default(author_id).await {
warn!("Failed to set default author: {:?}", e);
}
self.default_author = Some(author_id);
Ok(author_id)
}
Err(e) => {
error!("Failed to create author: {:?}", e);
Err(GuardianError::Storage(format!(
"Failed to create author: {:?}",
e
)))
}
}
}
}
}
pub async fn get_or_init_author(&mut self) -> Result<AuthorId> {
if let Some(author) = self.default_author {
Ok(author)
} else {
self.init_default_author().await
}
}
pub async fn create_doc(&self) -> Result<Doc> {
match self.docs.create().await {
Ok(doc) => {
info!("Created new document: {:?}", doc.id());
Ok(doc)
}
Err(e) => {
error!("Failed to create document: {:?}", e);
Err(GuardianError::Storage(format!(
"Failed to create document: {:?}",
e
)))
}
}
}
pub async fn open_doc(&self, namespace_id: NamespaceId) -> Result<Option<Doc>> {
match self.docs.open(namespace_id).await {
Ok(doc_option) => {
if let Some(ref doc) = doc_option {
debug!("Opened existing document: {:?}", doc.id());
}
Ok(doc_option)
}
Err(e) => {
error!("Failed to open document: {:?}", e);
Err(GuardianError::Storage(format!(
"Failed to open document: {:?}",
e
)))
}
}
}
pub async fn close_doc(&self, doc: &Doc) -> Result<()> {
match doc.close().await {
Ok(_) => {
debug!("Closed document: {:?}", doc.id());
Ok(())
}
Err(e) => {
error!("Failed to close document: {:?}", e);
Err(GuardianError::Storage(format!(
"Failed to close document: {:?}",
e
)))
}
}
}
pub async fn drop_doc(&self, namespace_id: NamespaceId) -> Result<()> {
match self.docs.drop_doc(namespace_id).await {
Ok(_) => {
info!("Dropped document: {:?}", namespace_id);
Ok(())
}
Err(e) => {
error!("Failed to drop document: {:?}", e);
Err(GuardianError::Storage(format!(
"Failed to drop document: {:?}",
e
)))
}
}
}
pub async fn set_bytes(
&self,
doc: &Doc,
author_id: AuthorId,
key: impl Into<Bytes>,
value: impl Into<Bytes>,
) -> Result<iroh_blobs::Hash> {
match doc.set_bytes(author_id, key, value).await {
Ok(hash) => {
debug!("Set bytes in document {:?}: hash={:?}", doc.id(), hash);
let hash_bytes = hash.as_bytes();
let result_hash = iroh_blobs::Hash::from_bytes(*hash_bytes);
Ok(result_hash)
}
Err(e) => {
error!("Failed to set bytes: {:?}", e);
Err(GuardianError::Storage(format!(
"Failed to set bytes: {:?}",
e
)))
}
}
}
pub async fn del(
&self,
doc: &Doc,
author_id: AuthorId,
key: impl Into<Bytes>,
) -> Result<usize> {
match doc.del(author_id, key).await {
Ok(count) => {
debug!("Deleted {} entries from document {:?}", count, doc.id());
Ok(count)
}
Err(e) => {
error!("Failed to delete: {:?}", e);
Err(GuardianError::Storage(format!("Failed to delete: {:?}", e)))
}
}
}
pub async fn get_one(&self, doc: &Doc, query: impl Into<Query>) -> Result<Option<Entry>> {
match doc.get_one(query).await {
Ok(entry_option) => {
if let Some(ref entry) = entry_option {
debug!(
"Got entry from document {:?}: key={:?}",
doc.id(),
String::from_utf8_lossy(entry.key())
);
}
Ok(entry_option)
}
Err(e) => {
error!("Failed to get entry: {:?}", e);
Err(GuardianError::Storage(format!(
"Failed to get entry: {:?}",
e
)))
}
}
}
pub async fn get_many(&self, doc: &Doc, query: impl Into<Query>) -> Result<Vec<Entry>> {
use futures::StreamExt;
match doc.get_many(query).await {
Ok(stream) => {
let entries: Vec<Entry> = stream
.filter_map(|result| async move {
match result {
Ok(entry) => Some(entry),
Err(e) => {
warn!("Error reading entry from stream: {:?}", e);
None
}
}
})
.collect()
.await;
debug!("Got {} entries from document {:?}", entries.len(), doc.id());
Ok(entries)
}
Err(e) => {
error!("Failed to get entries: {:?}", e);
Err(GuardianError::Storage(format!(
"Failed to get entries: {:?}",
e
)))
}
}
}
pub fn docs(&self) -> &Arc<Docs> {
&self.docs
}
pub async fn default_author_id(&self) -> Result<AuthorId> {
if let Some(author) = self.default_author {
Ok(author)
} else {
self.docs.author_default().await.map_err(|e| {
GuardianError::Storage(format!("No default author configured: {:?}", e))
})
}
}
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_client_creation() {
}
}