use anyhow::{anyhow, Result};
use async_trait::async_trait;
use cid::Cid;
use noosphere_core::{data::Did, view::Sphere};
use noosphere_storage::Storage;
use std::{
ops::{Deref, DerefMut},
sync::Arc,
};
use tokio::sync::{Mutex, OwnedMutexGuard};
use ucan::crypto::KeyMaterial;
use super::SphereContext;
#[cfg(not(target_arch = "wasm32"))]
pub trait HasConditionalSendSync: Send + Sync {}
#[cfg(not(target_arch = "wasm32"))]
impl<S> HasConditionalSendSync for S where S: Send + Sync {}
#[cfg(target_arch = "wasm32")]
pub trait HasConditionalSendSync {}
#[cfg(target_arch = "wasm32")]
impl<S> HasConditionalSendSync for S {}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait HasSphereContext<K, S>: Clone + HasConditionalSendSync
where
K: KeyMaterial + Clone + 'static,
S: Storage,
{
type SphereContext: Deref<Target = SphereContext<K, S>> + HasConditionalSendSync;
async fn sphere_context(&self) -> Result<Self::SphereContext>;
async fn identity(&self) -> Result<Did> {
let sphere_context = self.sphere_context().await?;
Ok(sphere_context.identity().clone())
}
async fn version(&self) -> Result<Cid> {
let identity = self.identity().await?;
let sphere_context = self.sphere_context().await?;
sphere_context
.db()
.get_version(&identity)
.await?
.ok_or_else(|| anyhow!("No version found for {}", identity))
}
async fn to_sphere(&self) -> Result<Sphere<S::BlockStore>> {
Ok(Sphere::at(
&self.version().await?,
&self.sphere_context().await?.db().to_block_store(),
))
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait HasMutableSphereContext<K, S>: HasSphereContext<K, S> + HasConditionalSendSync
where
K: KeyMaterial + Clone + 'static,
S: Storage,
{
type MutableSphereContext: Deref<Target = SphereContext<K, S>>
+ DerefMut<Target = SphereContext<K, S>>
+ HasConditionalSendSync;
async fn sphere_context_mut(&mut self) -> Result<Self::MutableSphereContext>;
async fn has_unsaved_changes(&self) -> Result<bool> {
let context = self.sphere_context().await?;
Ok(!context.mutation().is_empty())
}
async fn save(&mut self, additional_headers: Option<Vec<(String, String)>>) -> Result<Cid> {
let sphere = self.to_sphere().await?;
let mut sphere_context = self.sphere_context_mut().await?;
let sphere_identity = sphere_context.identity().clone();
let mut revision = sphere.apply_mutation(sphere_context.mutation()).await?;
match additional_headers {
Some(headers) if !headers.is_empty() => revision.memo.replace_headers(headers),
_ if sphere_context.mutation().is_empty() => return Err(anyhow!("No changes to save")),
_ => (),
}
let new_sphere_version = revision
.sign(
&sphere_context.author().key,
sphere_context.author().authorization.as_ref(),
)
.await?;
sphere_context
.db_mut()
.set_version(&sphere_identity, &new_sphere_version)
.await?;
sphere_context.db_mut().flush().await?;
sphere_context.mutation_mut().reset();
Ok(new_sphere_version)
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<K, S> HasSphereContext<K, S> for Arc<Mutex<SphereContext<K, S>>>
where
K: KeyMaterial + Clone + 'static,
S: Storage + 'static,
{
type SphereContext = OwnedMutexGuard<SphereContext<K, S>>;
async fn sphere_context(&self) -> Result<Self::SphereContext> {
Ok(self.clone().lock_owned().await)
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<K, S> HasSphereContext<K, S> for Arc<SphereContext<K, S>>
where
K: KeyMaterial + Clone + 'static,
S: Storage,
{
type SphereContext = Arc<SphereContext<K, S>>;
async fn sphere_context(&self) -> Result<Self::SphereContext> {
Ok(self.clone())
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<K, S> HasMutableSphereContext<K, S> for Arc<Mutex<SphereContext<K, S>>>
where
K: KeyMaterial + Clone + 'static,
S: Storage + 'static,
{
type MutableSphereContext = OwnedMutexGuard<SphereContext<K, S>>;
async fn sphere_context_mut(&mut self) -> Result<Self::MutableSphereContext> {
self.sphere_context().await
}
}