Skip to main content

coven/
builder_read_only.rs

1//! Open a store for reading only, beside a handle that has it open.
2
3use super::*;
4
5impl CovenBuilder {
6    /// Opens the store for reading only, alongside a handle that has it open.
7    /// Its shared lock protects both read connections and local cache metadata.
8    /// It runs no migration and refuses a database whose schema is newer,
9    /// whose coven tables need migrating, or whose indexes were built with
10    /// other versions of the app's collations and functions.
11    /// File reads connect using saved settings and custody credentials when present.
12    pub async fn open_read_only(
13        mut self,
14        store: StoreId,
15    ) -> Result<CovenReadHandle, ReadOnlyOpenError> {
16        let directory = self.directories.store_dir(&store);
17        let ids = self.ids.clone();
18        let clock = self.clock.clone();
19        // A read-only handle cannot persist a cooldown; it holds its own requests.
20        let waits = Arc::new(coven_sync::RetryWaits::in_memory(clock.clone()));
21        let connector = self.connector();
22        let transfers = self.transfers.clone();
23        let database = self.database(directory.clone())?;
24        // Before the swap's marker exists, only the record shows that a
25        // replacement is moving the files a reader would open (ยง19.3).
26        refuse_unfinished_replacement(&directory, store)?;
27        refuse_unfinished_deletion(&directory, store)?;
28        let database = database.open_read_only().await?;
29        if let Some(identity) = database.store_state().await?.store {
30            check_store_settings(&identity, &directory)?;
31        }
32        let device = directory.settings().map_err(CovenError::from)?.device_id;
33        let oauth = self.oauth_exchange();
34        let in_directory = directory.clone();
35        let bugs = directory.bugs().clone();
36        let (credentials, data) =
37            crate::task::blocking_completing("read the saved credentials", bugs, move || {
38                let credentials = device_credentials(self.keychain()?, store, in_directory, oauth);
39                let data = credentials.connection()?;
40                Ok::<_, SyncError>((credentials, data))
41            })
42            .await
43            .map_err(SyncError::from)??;
44        let storage: Option<Arc<dyn Storage>> = match data {
45            Some(data) => {
46                let storage = connector
47                    .connect(
48                        data.location,
49                        coven_storage::ProviderAuth::new(data.credentials, credentials),
50                        device,
51                        Arc::new(crate::storage::NoPublications),
52                        waits.clone(),
53                        transfers.clone(),
54                    )
55                    .await
56                    .map_err(SyncError::from)?;
57                storage.check_location_claim(store).await.map_err(SyncError::from)?;
58                Some(storage)
59            }
60            None => None,
61        };
62        let files = coven_sync::Files::read_only(
63            coven_database::FileDatabase::new(database.clone()),
64            directory,
65            storage,
66            Arc::new(coven_sync::SequenceState::new()),
67            waits,
68            ids,
69            transfers,
70            clock,
71            coven_foundation::network_watch::NetworkWatch::unwatched().view(),
72        );
73        Ok(CovenReadHandle::new(database, files))
74    }
75}