use itertools::Itertools as _; use sqlx::sqlite::SqlitePool; use super::Snapshot; use crate::{ conversation::{self, repo::Provider as _}, db::NotFound, event::{Event, Sequence, repo::Provider as _}, message::{self, repo::Provider as _}, name, user::{self, repo::Provider as _}, vapid::{self, repo::Provider as _}, }; pub struct Boot { db: SqlitePool, } impl Boot { pub const fn new(db: SqlitePool) -> Self { Self { db } } pub async fn snapshot(&self) -> Result { let mut tx = self.db.begin().await?; let resume_point = tx.sequence().current().await?; let users = tx.users().all(resume_point).await?; let conversations = tx.conversations().all(resume_point).await?; let messages = tx.messages().all(resume_point).await?; let vapid = tx.vapid().current().await.optional()?; tx.commit().await?; let user_events = users .iter() .map(user::History::events) .kmerge_by(Sequence::merge) .filter(Sequence::up_to(resume_point)) .map(Event::from); let conversation_events = conversations .iter() .map(conversation::History::events) .kmerge_by(Sequence::merge) .filter(Sequence::up_to(resume_point)) .map(Event::from); let message_events = messages .iter() .map(message::History::events) .kmerge_by(Sequence::merge) .filter(Sequence::up_to(resume_point)) .map(Event::from); let vapid_events = vapid .iter() .flat_map(vapid::History::events) .filter(Sequence::up_to(resume_point)) .map(Event::from); let events = user_events .merge_by(conversation_events, Sequence::merge) .merge_by(message_events, Sequence::merge) .merge_by(vapid_events, Sequence::merge) .collect(); Ok(Snapshot { resume_point, events, }) } } #[derive(Debug, thiserror::Error)] #[error(transparent)] pub enum Error { Database(#[from] sqlx::Error), Name(#[from] name::Error), Ecdsa(#[from] p256::ecdsa::Error), Pkcs8(#[from] p256::pkcs8::Error), } impl From for Error { fn from(error: user::repo::LoadError) -> Self { use user::repo::LoadError; match error { LoadError::Name(error) => error.into(), LoadError::Database(error) => error.into(), } } } impl From for Error { fn from(error: conversation::repo::LoadError) -> Self { use conversation::repo::LoadError; match error { LoadError::Name(error) => error.into(), LoadError::Database(error) => error.into(), } } } impl From for Error { fn from(error: vapid::repo::Error) -> Self { use vapid::repo::Error; match error { Error::Database(error) => error.into(), Error::Ecdsa(error) => error.into(), Error::Pkcs8(error) => error.into(), } } }