noosphere_sphere/
has.rs

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/// Any container that can provide non-mutable access to a [SphereContext]
34/// should implement [HasSphereContext]. The most common example of something
35/// that may implement this trait is an `Arc<SphereContext<_, _>>`. Implementors
36/// of this trait will automatically implement other traits that provide
37/// convience methods for accessing different parts of the sphere, such as
38/// content and petnames.
39#[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    /// The type of the internal read-only [SphereContext]
46    type SphereContext: Deref<Target = SphereContext<S>> + HasConditionalSendSync;
47
48    /// Get the [SphereContext] that is made available by this container.
49    async fn sphere_context(&self) -> Result<Self::SphereContext>;
50
51    /// Get the DID identity of the sphere that this FS view is reading from and
52    /// writing to
53    async fn identity(&self) -> Result<Did> {
54        let sphere_context = self.sphere_context().await?;
55
56        Ok(sphere_context.identity().clone())
57    }
58
59    /// The CID of the most recent local version of this sphere
60    async fn version(&self) -> Result<Link<MemoIpld>> {
61        self.sphere_context().await?.version().await
62    }
63
64    /// Get a data view into the sphere at the current revision
65    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    /// Create a new [SphereContext] via [SphereContext::with_author] and wrap it in the same
71    /// [HasSphereContext] implementation, returning the result
72    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    /// Wrap a given [SphereContext] in this [HasSphereContext]
77    async fn wrap(sphere_context: SphereContext<S>) -> Self;
78}
79
80/// Any container that can provide mutable access to a [SphereContext] should
81/// implement [HasMutableSphereContext]. The most common example of something
82/// that may implement this trait is `Arc<Mutex<SphereContext<_, _>>>`.
83/// Implementors of this trait will automatically implement other traits that
84/// provide convenience methods for modifying the contents, petnames and other
85/// aspects of a sphere.
86#[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    /// The type of the internal mutable [SphereContext]
93    type MutableSphereContext: Deref<Target = SphereContext<S>>
94        + DerefMut<Target = SphereContext<S>>
95        + HasConditionalSendSync;
96
97    /// Get a mutable reference to the [SphereContext] that is wrapped by this
98    /// container.
99    async fn sphere_context_mut(&mut self) -> Result<Self::MutableSphereContext>;
100
101    /// Returns true if any changes have been made to the underlying
102    /// [SphereContext] that have not been committed to the associated sphere
103    /// yet (according to local history).
104    async fn has_unsaved_changes(&self) -> Result<bool> {
105        let context = self.sphere_context().await?;
106        Ok(!context.mutation().is_empty())
107    }
108
109    /// Commits a series of writes to the sphere and signs the new version. The
110    /// new version [Link<MemoIpld>] of the sphere is returned. This method must
111    /// be invoked in order to update the local history of the sphere with any
112    /// changes that have been made.
113    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}