Skip to main content

coven_database/
database_device.rs

1//! Device-reset notices and committed member lists (ยง19.3, E5).
2
3use super::{Database, DbError};
4use crate::{LiveQuery, NewOperation, PendingValue, ProviderAccessChange};
5use coven_foundation::files::DeviceReset;
6use coven_foundation::locks::read_unpoisoned;
7
8impl Database {
9    /// Commit carried provider cleanup with the replacement notice before
10    /// publication. A resumed bootstrap cannot import the same work twice.
11    pub async fn record_device_reset<P: PendingValue>(
12        &self,
13        reset: DeviceReset,
14        operations: Vec<NewOperation>,
15        provider_access: Vec<ProviderAccessChange>,
16    ) -> Result<(), DbError> {
17        self.call(move |inner| {
18            inner.access.with_writer(|writer| {
19                writer.transaction(|db| {
20                    if crate::device_reset::read(db)? == Some(reset) {
21                        return Ok(());
22                    }
23                    for operation in &operations {
24                        crate::operation::insert(db, operation)?;
25                    }
26                    crate::provider_access::adopt::<P>(db, &provider_access)?;
27                    crate::device_reset::record(db, &reset)
28                })
29            })
30        })
31        .await
32    }
33
34    /// This installation's device id, or `StoreClosed` once closed.
35    pub(crate) fn local_device(&self) -> Result<coven_foundation::id_source::DeviceId, DbError> {
36        let guard = read_unpoisoned(&self.inner);
37        Ok(guard.as_ref().ok_or(DbError::StoreClosed)?.access.device)
38    }
39
40    /// Store members and every device they registered, from the committed
41    /// sequence prefix (E9). `subscribe_members` runs the same query.
42    pub async fn members(&self) -> Result<Vec<crate::MemberInfo>, DbError> {
43        let device = self.local_device()?;
44        self.read(move |sql| sql.members(device)).await
45    }
46
47    /// The same members query, live (E4).
48    pub fn subscribe_members<E>(&self) -> LiveQuery<Vec<crate::MemberInfo>, E>
49    where
50        E: From<DbError> + coven_foundation::task::CarriesBug + Send + 'static,
51    {
52        let device = self.local_device().ok();
53        self.subscribe(move |sql| sql.members(device.ok_or(DbError::StoreClosed)?).map_err(E::from))
54    }
55
56    /// The circles this installation's member belongs to (E12).
57    pub async fn circles(&self) -> Result<Vec<crate::Circle>, DbError> {
58        let device = self.local_device()?;
59        self.read(move |sql| sql.circles(device)).await
60    }
61
62    /// The same circle-list query, live (E4).
63    pub fn subscribe_circles<E>(&self) -> LiveQuery<Vec<crate::Circle>, E>
64    where
65        E: From<DbError> + coven_foundation::task::CarriesBug + Send + 'static,
66    {
67        let device = self.local_device().ok();
68        self.subscribe(move |sql| sql.circles(device.ok_or(DbError::StoreClosed)?).map_err(E::from))
69    }
70
71    /// A circle's members who remain in the store; none for an absent or
72    /// deleted circle (E12).
73    pub async fn circle_members(
74        &self,
75        circle: coven_foundation::id_source::CircleId,
76    ) -> Result<Vec<crate::CircleMemberInfo>, DbError> {
77        let device = self.local_device()?;
78        self.read(move |sql| sql.circle_members(device, circle)).await
79    }
80
81    /// The same circle-members query, live (E4).
82    pub fn subscribe_circle_members<E>(
83        &self,
84        circle: coven_foundation::id_source::CircleId,
85    ) -> LiveQuery<Vec<crate::CircleMemberInfo>, E>
86    where
87        E: From<DbError> + coven_foundation::task::CarriesBug + Send + 'static,
88    {
89        let device = self.local_device().ok();
90        self.subscribe(move |sql| {
91            sql.circle_members(device.ok_or(DbError::StoreClosed)?, circle).map_err(E::from)
92        })
93    }
94
95    /// The latest completed replacement of this installation, or None (E5).
96    pub async fn device_reset(&self) -> Result<Option<DeviceReset>, DbError> {
97        self.read(|sql| sql.device_reset()).await
98    }
99
100    /// The same pre-built query, observing committed changes (E4).
101    pub fn subscribe_device_reset<E>(&self) -> LiveQuery<Option<DeviceReset>, E>
102    where
103        E: From<DbError> + coven_foundation::task::CarriesBug + Send + 'static,
104    {
105        self.subscribe(|sql| sql.device_reset().map_err(E::from))
106    }
107}
108
109#[cfg(test)]
110#[path = "database_device_tests.rs"]
111mod tests;