Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122use rusqlite::{OptionalExtension, params};
use crate::{ domain::{AuditEvent, DeviceId, FolderId, FolderRoster}, storage::{StateStore, audit::append_audit_event},};
impl StateStore { #[cfg(test)] pub fn replace_capability_and_roster( &self, folder_id: FolderId, roster: &FolderRoster, ) -> anyhow::Result<()> { self.replace_capability_and_roster_with_optional_audit(folder_id, roster, None) }
pub fn replace_capability_and_roster_with_audit( &self, folder_id: FolderId, roster: &FolderRoster, audit_event: &AuditEvent, ) -> anyhow::Result<()> { self.replace_capability_and_roster_with_optional_audit(folder_id, roster, Some(audit_event)) }
fn replace_capability_and_roster_with_optional_audit( &self, folder_id: FolderId, roster: &FolderRoster, audit_event: Option<&AuditEvent>, ) -> anyhow::Result<()> { if roster.folder_id != folder_id { anyhow::bail!("roster belongs to another folder"); } roster.validate()?; let transaction = self.connection.unchecked_transaction()?; transaction.execute( "UPDATE folders SET capability = ?1 WHERE id = ?2", params![roster.capability, folder_id.to_string()], )?; transaction.execute( "INSERT INTO folder_rosters (folder_id, epoch, roster) VALUES (?1, ?2, ?3) ON CONFLICT(folder_id) DO UPDATE SET epoch = excluded.epoch, roster = excluded.roster", params![folder_id.to_string(), i64::try_from(roster.epoch)?, serde_json::to_string(roster)?], )?; if let Some(audit_event) = audit_event { append_audit_event(&transaction, audit_event)?; } transaction.commit()?; Ok(()) }
pub fn save_roster(&self, roster: &FolderRoster) -> anyhow::Result<()> { self.save_roster_with_optional_audit(roster, None) }
pub fn save_roster_with_audit( &self, roster: &FolderRoster, audit_event: &AuditEvent, ) -> anyhow::Result<()> { self.save_roster_with_optional_audit(roster, Some(audit_event)) }
fn save_roster_with_optional_audit( &self, roster: &FolderRoster, audit_event: Option<&AuditEvent>, ) -> anyhow::Result<()> { roster.validate()?; let transaction = self.connection.unchecked_transaction()?; transaction.execute( "INSERT INTO folder_rosters (folder_id, epoch, roster) VALUES (?1, ?2, ?3) ON CONFLICT(folder_id) DO UPDATE SET epoch = excluded.epoch, roster = excluded.roster", params![roster.folder_id.to_string(), i64::try_from(roster.epoch)?, serde_json::to_string(roster)?], )?; if let Some(audit_event) = audit_event { append_audit_event(&transaction, audit_event)?; } transaction.commit()?; Ok(()) }
pub fn load_roster(&self, folder_id: FolderId) -> anyhow::Result<Option<FolderRoster>> { let serialized: Option<String> = self .connection .query_row( "SELECT roster FROM folder_rosters WHERE folder_id = ?1", params![folder_id.to_string()], |row| row.get(0), ) .optional()?; serialized .map(|value| serde_json::from_str(&value).map_err(Into::into)) .transpose() }
pub fn save_roster_if_newer(&self, roster: &FolderRoster) -> anyhow::Result<bool> { roster.validate()?; let Some(current) = self.load_roster(roster.folder_id)? else { self.save_roster(roster)?; return Ok(true); }; if current.owner_device_id != roster.owner_device_id { anyhow::bail!("received a roster from an unexpected folder owner"); } if roster.epoch > current.epoch { self.save_roster(roster)?; return Ok(true); } Ok(false) }
pub fn next_counter(&self, folder_id: FolderId, device_id: &DeviceId) -> anyhow::Result<u64> { let next: i64 = self.connection.query_row( "INSERT INTO counters (folder_id, device_id, counter) VALUES (?1, ?2, 1) ON CONFLICT(folder_id, device_id) DO UPDATE SET counter = counters.counter + 1 RETURNING counter", params![folder_id.to_string(), device_id], |row| row.get(0), )?; u64::try_from(next).map_err(Into::into) }}