1use crate::{database, sqlite::DatabaseConnection, Database, DbError, LostValue, Mode, Writer};
4use coven_foundation::task::CarriesBug;
5use rusqlite::{Params, Row};
6use std::{
7 any::Any,
8 future::Future,
9 pin::Pin,
10 task::{Context, Poll},
11};
12
13pub struct SqlReadContext<'connection> {
16 database: &'connection DatabaseConnection,
17}
18
19impl<'connection> SqlReadContext<'connection> {
20 pub(crate) fn new(database: &'connection DatabaseConnection) -> Self {
21 Self { database }
22 }
23
24 pub fn query_row<T, P, F>(&self, sql: &str, params: P, map: F) -> rusqlite::Result<T>
26 where
27 P: Params,
28 F: FnOnce(&Row<'_>) -> rusqlite::Result<T>,
29 {
30 self.database.app_query_row(sql, params, map)
31 }
32
33 pub fn query<T, P, F>(&self, sql: &str, params: P, map: F) -> rusqlite::Result<Vec<T>>
35 where
36 P: Params,
37 F: FnMut(&Row<'_>) -> rusqlite::Result<T>,
38 {
39 self.database.app_query(sql, params, map)
40 }
41
42 pub(crate) fn device_only_source(
45 &self,
46 device: coven_foundation::id_source::DeviceId,
47 file: coven_foundation::id_source::FileId,
48 ) -> Result<Option<crate::file_ref::LocalSource>, crate::DbError> {
49 crate::file_ref::device_only_source(self.database, device, file)
50 }
51
52 pub(crate) fn file_ref(
53 &self,
54 file: coven_foundation::id_source::FileId,
55 ) -> Result<crate::FileRef, crate::DbError> {
56 crate::file_ref::read(self.database, file)
57 }
58
59 pub(crate) fn user_file(
60 &self,
61 file: coven_foundation::id_source::FileId,
62 ) -> Result<Option<crate::UserFile>, crate::DbError> {
63 crate::user_file::read(self.database, file)
64 }
65
66 pub(crate) fn lost_values(&self) -> Result<Vec<LostValue>, DbError> {
67 self.database.lost_values()
68 }
69
70 pub(crate) fn pending<P: crate::PendingValue>(&self) -> Result<Vec<P>, DbError> {
71 crate::pending::read(self.database)
72 }
73
74 pub(crate) fn pin_progress(
75 &self,
76 files: &[crate::FileRef],
77 device: coven_foundation::id_source::DeviceId,
78 ) -> Result<crate::PinProgress, DbError> {
79 crate::cache_progress::pin_progress(self.database, files, device)
80 }
81
82 pub(crate) fn cache_budgets(&self) -> Result<Vec<crate::NamespaceBytes>, DbError> {
83 crate::database::local_bytes::budgets(self.database)
84 }
85
86 pub(crate) fn cache_bytes(&self) -> Result<(Vec<crate::NamespaceBytes>, u64), DbError> {
88 Ok((
89 crate::database::local_bytes::budgets(self.database)?,
90 crate::database::local_bytes::partials(self.database)?,
91 ))
92 }
93
94 pub(crate) fn files_pinned(
95 &self,
96 files: &[coven_foundation::id_source::FileId],
97 ) -> Result<Vec<Option<bool>>, DbError> {
98 crate::cache_progress::files_pinned(self.database, files)
99 }
100
101 pub(crate) fn unsent(&self) -> Result<crate::UnsentWork, DbError> {
102 crate::file_queue::unsent(self.database)
103 }
104
105 pub(crate) fn sync_facts<P: crate::PendingValue + Clone>(
106 &self,
107 ) -> Result<crate::SyncFacts<P>, DbError> {
108 crate::sync_facts::read(self.database)
109 }
110
111 #[cfg(test)]
113 pub(crate) fn sync_status_row(&self) -> Result<crate::SyncStatusRow, DbError> {
114 crate::internal_schema::sync_status::read(self.database)
115 }
116
117 pub(crate) fn upload_queue(&self) -> Result<crate::UploadQueueState, DbError> {
118 crate::file_queue::state(self.database)
119 }
120
121 pub(crate) fn device_reset(
122 &self,
123 ) -> Result<Option<coven_foundation::files::DeviceReset>, DbError> {
124 crate::device_reset::read(self.database)
125 }
126
127 pub(crate) fn store_name(&self) -> Result<String, DbError> {
128 crate::store_state_tables::name(self.database)
129 }
130
131 pub(crate) fn named_store(&self) -> Result<(String, coven_merge::Slot), DbError> {
132 crate::store_state_tables::named(self.database)
133 }
134
135 pub(crate) fn joined_invitee(&self) -> Result<Option<String>, DbError> {
136 crate::joined_invitee::read(self.database)
137 }
138
139 pub(crate) fn members(
140 &self,
141 device: coven_foundation::id_source::DeviceId,
142 ) -> Result<Vec<crate::MemberInfo>, DbError> {
143 crate::members::members(self.database, device)
144 }
145
146 pub(crate) fn circles(
147 &self,
148 device: coven_foundation::id_source::DeviceId,
149 ) -> Result<Vec<crate::Circle>, DbError> {
150 crate::members::circles(self.database, device)
151 }
152
153 pub(crate) fn circle_members(
154 &self,
155 device: coven_foundation::id_source::DeviceId,
156 circle: coven_foundation::id_source::CircleId,
157 ) -> Result<Vec<crate::CircleMemberInfo>, DbError> {
158 crate::members::circle_members(self.database, device, circle)
159 }
160
161 #[cfg(any(test, feature = "test-utils"))]
162 pub(crate) fn store_state(&self) -> Result<crate::StoreState, DbError> {
163 crate::store_state_tables::read(self.database)
164 }
165
166 #[cfg(any(test, feature = "test-utils"))]
167 pub(crate) fn retry_wait(
168 &self,
169 subject: crate::RetrySubject,
170 ) -> Result<Option<crate::RetryWait>, DbError> {
171 crate::retry::find(self.database, subject)
172 }
173}
174
175#[must_use = "reads run when awaited"]
178pub struct Read<'a, F, M: Mode = Writer> {
179 database: &'a Database<M>,
180 state: ReadState<F>,
181}
182
183enum ReadState<F> {
186 Pending(F),
187 Running(Box<dyn Any + Send>),
188 Finished,
189}
190
191impl<'a, F, R, E, M: Mode> Read<'a, F, M>
192where
193 F: FnOnce(SqlReadContext<'_>) -> Result<R, E> + Send + 'static,
194 R: Send + 'static,
195 E: From<DbError> + CarriesBug + Send + 'static,
196{
197 pub(crate) fn new(database: &'a Database<M>, read: F) -> Self {
198 Self { database, state: ReadState::Pending(read) }
199 }
200
201 pub async fn process<P, T>(self, process: P) -> Result<T, E>
204 where
205 P: FnOnce(R) -> Result<T, E> + Send + 'static,
206 T: Send + 'static,
207 {
208 let bugs = self.database.bugs().clone();
209 let result = self.await?;
210 database::process(&bugs, move || process(result)).await
211 }
212}
213
214impl<F, M: Mode> Unpin for Read<'_, F, M> {}
216
217impl<F, R, E, M: Mode> Future for Read<'_, F, M>
218where
219 F: FnOnce(SqlReadContext<'_>) -> Result<R, E> + Send + 'static,
220 R: Send + 'static,
221 E: From<DbError> + CarriesBug + Send + 'static,
222{
223 type Output = Result<R, E>;
224
225 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
226 let this = self.get_mut();
227 let mut task: Box<dyn Any + Send> =
228 match std::mem::replace(&mut this.state, ReadState::Finished) {
229 ReadState::Pending(read) => Box::new(this.database.start_read(read)),
230 ReadState::Running(task) => task,
231 ReadState::Finished => panic!("read polled after completion"),
232 };
233 let running =
234 task.downcast_mut::<RunningRead<R, E>>().expect("read task matches its closure result");
235 match Pin::new(running).poll(cx) {
236 Poll::Pending => {
237 this.state = ReadState::Running(task);
238 Poll::Pending
239 }
240 Poll::Ready(result) => Poll::Ready(result),
241 }
242 }
243}
244
245pub(crate) struct RunningRead<R, E> {
248 call: coven_foundation::task::CompletingTask<Result<R, E>>,
249}
250
251impl<R, E> RunningRead<R, E> {
252 pub(crate) fn new(call: coven_foundation::task::CompletingTask<Result<R, E>>) -> Self {
253 Self { call }
254 }
255}
256
257impl<R, E: From<DbError>> Future for RunningRead<R, E> {
258 type Output = Result<R, E>;
259
260 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
261 Pin::new(&mut self.get_mut().call).poll(cx).map(database::finish_blocking)
262 }
263}
264
265#[cfg(test)]
266#[path = "read_tests.rs"]
267pub(crate) mod tests;