1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3use noosphere_core::{
4 authority::Author,
5 data::{Did, Link, MemoIpld},
6 view::Sphere,
7};
8use noosphere_storage::{SphereDb, Storage};
9use std::{
10 ops::{Deref, DerefMut},
11 sync::Arc,
12};
13use tokio::sync::{Mutex, OwnedMutexGuard};
14
15use crate::SphereContextKey;
16
17use super::SphereContext;
18
19#[allow(missing_docs)]
20#[cfg(not(target_arch = "wasm32"))]
21pub trait HasConditionalSendSync: Send + Sync {}
22
23#[cfg(not(target_arch = "wasm32"))]
24impl<S> HasConditionalSendSync for S where S: Send + Sync {}
25
26#[allow(missing_docs)]
27#[cfg(target_arch = "wasm32")]
28pub trait HasConditionalSendSync {}
29
30#[cfg(target_arch = "wasm32")]
31impl<S> HasConditionalSendSync for S {}
32
33#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
40#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
41pub trait HasSphereContext<S>: Clone + HasConditionalSendSync
42where
43 S: Storage,
44{
45 type SphereContext: Deref<Target = SphereContext<S>> + HasConditionalSendSync;
47
48 async fn sphere_context(&self) -> Result<Self::SphereContext>;
50
51 async fn identity(&self) -> Result<Did> {
54 let sphere_context = self.sphere_context().await?;
55
56 Ok(sphere_context.identity().clone())
57 }
58
59 async fn version(&self) -> Result<Link<MemoIpld>> {
61 self.sphere_context().await?.version().await
62 }
63
64 async fn to_sphere(&self) -> Result<Sphere<SphereDb<S>>> {
66 let version = self.version().await?;
67 Ok(Sphere::at(&version, self.sphere_context().await?.db()))
68 }
69
70 async fn with_author(&self, author: &Author<SphereContextKey>) -> Result<Self> {
73 Ok(Self::wrap(self.sphere_context().await?.with_author(author).await?).await)
74 }
75
76 async fn wrap(sphere_context: SphereContext<S>) -> Self;
78}
79
80#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
87#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
88pub trait HasMutableSphereContext<S>: HasSphereContext<S> + HasConditionalSendSync
89where
90 S: Storage,
91{
92 type MutableSphereContext: Deref<Target = SphereContext<S>>
94 + DerefMut<Target = SphereContext<S>>
95 + HasConditionalSendSync;
96
97 async fn sphere_context_mut(&mut self) -> Result<Self::MutableSphereContext>;
100
101 async fn has_unsaved_changes(&self) -> Result<bool> {
105 let context = self.sphere_context().await?;
106 Ok(!context.mutation().is_empty())
107 }
108
109 async fn save(
114 &mut self,
115 additional_headers: Option<Vec<(String, String)>>,
116 ) -> Result<Link<MemoIpld>> {
117 let sphere = self.to_sphere().await?;
118 let mut sphere_context = self.sphere_context_mut().await?;
119 let sphere_identity = sphere_context.identity().clone();
120 let mut revision = sphere.apply_mutation(sphere_context.mutation()).await?;
121
122 match additional_headers {
123 Some(headers) if !headers.is_empty() => revision.memo.replace_headers(headers),
124 _ if sphere_context.mutation().is_empty() => return Err(anyhow!("No changes to save")),
125 _ => (),
126 }
127
128 let new_sphere_version = revision
129 .sign(
130 &sphere_context.author().key,
131 sphere_context.author().authorization.as_ref(),
132 )
133 .await?;
134
135 sphere_context
136 .db_mut()
137 .set_version(&sphere_identity, &new_sphere_version)
138 .await?;
139 sphere_context.db_mut().flush().await?;
140 sphere_context.mutation_mut().reset();
141
142 Ok(new_sphere_version)
143 }
144}
145
146#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
147#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
148impl<S> HasSphereContext<S> for Arc<Mutex<SphereContext<S>>>
149where
150 S: Storage + 'static,
151{
152 type SphereContext = OwnedMutexGuard<SphereContext<S>>;
153
154 async fn sphere_context(&self) -> Result<Self::SphereContext> {
155 Ok(self.clone().lock_owned().await)
156 }
157
158 async fn wrap(sphere_context: SphereContext<S>) -> Self {
159 Arc::new(Mutex::new(sphere_context))
160 }
161}
162
163#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
164#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
165impl<S, T> HasSphereContext<S> for Box<T>
166where
167 T: HasSphereContext<S>,
168 S: Storage + 'static,
169{
170 type SphereContext = T::SphereContext;
171
172 async fn sphere_context(&self) -> Result<Self::SphereContext> {
173 T::sphere_context(self).await
174 }
175
176 async fn wrap(sphere_context: SphereContext<S>) -> Self {
177 Box::new(T::wrap(sphere_context).await)
178 }
179}
180
181#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
182#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
183impl<S> HasSphereContext<S> for Arc<SphereContext<S>>
184where
185 S: Storage,
186{
187 type SphereContext = Arc<SphereContext<S>>;
188
189 async fn sphere_context(&self) -> Result<Self::SphereContext> {
190 Ok(self.clone())
191 }
192
193 async fn wrap(sphere_context: SphereContext<S>) -> Self {
194 Arc::new(sphere_context)
195 }
196}
197
198#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
199#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
200impl<S> HasMutableSphereContext<S> for Arc<Mutex<SphereContext<S>>>
201where
202 S: Storage + 'static,
203{
204 type MutableSphereContext = OwnedMutexGuard<SphereContext<S>>;
205
206 async fn sphere_context_mut(&mut self) -> Result<Self::MutableSphereContext> {
207 self.sphere_context().await
208 }
209}
210
211#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
212#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
213impl<S, T> HasMutableSphereContext<S> for Box<T>
214where
215 T: HasMutableSphereContext<S>,
216 S: Storage + 'static,
217{
218 type MutableSphereContext = T::MutableSphereContext;
219
220 async fn sphere_context_mut(&mut self) -> Result<Self::MutableSphereContext> {
221 T::sphere_context_mut(self).await
222 }
223}