use anyhow::{anyhow, Result};
use async_trait::async_trait;
use noosphere_core::{
authority::Author,
data::{Did, Link, MemoIpld},
view::Sphere,
};
use noosphere_storage::{SphereDb, Storage};
use std::{
ops::{Deref, DerefMut},
sync::Arc,
};
use tokio::sync::{Mutex, OwnedMutexGuard};
use crate::SphereContextKey;
use super::SphereContext;
#[allow(missing_docs)]
#[cfg(not(target_arch = "wasm32"))]
pub trait HasConditionalSendSync: Send + Sync {}
#[cfg(not(target_arch = "wasm32"))]
impl<S> HasConditionalSendSync for S where S: Send + Sync {}
#[allow(missing_docs)]
#[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<S>: Clone + HasConditionalSendSync
where
S: Storage,
{
type SphereContext: Deref<Target = SphereContext<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<Link<MemoIpld>> {
self.sphere_context().await?.version().await
}
async fn to_sphere(&self) -> Result<Sphere<SphereDb<S>>> {
let version = self.version().await?;
Ok(Sphere::at(&version, self.sphere_context().await?.db()))
}
async fn with_author(&self, author: &Author<SphereContextKey>) -> Result<Self> {
Ok(Self::wrap(self.sphere_context().await?.with_author(author).await?).await)
}
async fn wrap(sphere_context: SphereContext<S>) -> Self;
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait HasMutableSphereContext<S>: HasSphereContext<S> + HasConditionalSendSync
where
S: Storage,
{
type MutableSphereContext: Deref<Target = SphereContext<S>>
+ DerefMut<Target = SphereContext<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<Link<MemoIpld>> {
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<S> HasSphereContext<S> for Arc<Mutex<SphereContext<S>>>
where
S: Storage + 'static,
{
type SphereContext = OwnedMutexGuard<SphereContext<S>>;
async fn sphere_context(&self) -> Result<Self::SphereContext> {
Ok(self.clone().lock_owned().await)
}
async fn wrap(sphere_context: SphereContext<S>) -> Self {
Arc::new(Mutex::new(sphere_context))
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<S, T> HasSphereContext<S> for Box<T>
where
T: HasSphereContext<S>,
S: Storage + 'static,
{
type SphereContext = T::SphereContext;
async fn sphere_context(&self) -> Result<Self::SphereContext> {
T::sphere_context(self).await
}
async fn wrap(sphere_context: SphereContext<S>) -> Self {
Box::new(T::wrap(sphere_context).await)
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<S> HasSphereContext<S> for Arc<SphereContext<S>>
where
S: Storage,
{
type SphereContext = Arc<SphereContext<S>>;
async fn sphere_context(&self) -> Result<Self::SphereContext> {
Ok(self.clone())
}
async fn wrap(sphere_context: SphereContext<S>) -> Self {
Arc::new(sphere_context)
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<S> HasMutableSphereContext<S> for Arc<Mutex<SphereContext<S>>>
where
S: Storage + 'static,
{
type MutableSphereContext = OwnedMutexGuard<SphereContext<S>>;
async fn sphere_context_mut(&mut self) -> Result<Self::MutableSphereContext> {
self.sphere_context().await
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<S, T> HasMutableSphereContext<S> for Box<T>
where
T: HasMutableSphereContext<S>,
S: Storage + 'static,
{
type MutableSphereContext = T::MutableSphereContext;
async fn sphere_context_mut(&mut self) -> Result<Self::MutableSphereContext> {
T::sphere_context_mut(self).await
}
}