use std::collections::HashMap; use ruma::{ events::{ AnyGlobalAccountDataEvent, AnyGlobalAccountDataEventContent, AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent, GlobalAccountDataEventType, RoomAccountDataEventType, }, serde::Raw, RoomId, UserId, }; use serde::{Deserialize, Serialize}; use crate::{Error, Result}; mod data; pub(crate) use data::Data; pub(crate) struct Service { pub(crate) db: &'static dyn Data, } pub(crate) fn raw_global_event_to_parts( event: &Raw, ) -> serde_json::Result<( GlobalAccountDataEventType, Raw, )> { #[derive(Deserialize)] struct Parts { #[serde(rename = "type")] event_type: GlobalAccountDataEventType, content: Raw, } let parts = event.deserialize_as::()?; Ok((parts.event_type, parts.content)) } pub(crate) fn raw_global_event_from_parts( event_type: &GlobalAccountDataEventType, content: &Raw, ) -> Raw { #[derive(Serialize)] struct Parts<'a> { #[serde(rename = "type")] event_type: &'a GlobalAccountDataEventType, content: &'a Raw, } Raw::new(&Parts { event_type, content, }) .expect("json serialization should always succeed") .cast::() } pub(crate) fn raw_room_event_to_parts( event: &Raw, ) -> serde_json::Result<( RoomAccountDataEventType, Raw, )> { #[derive(Deserialize)] struct Parts { #[serde(rename = "type")] event_type: RoomAccountDataEventType, content: Raw, } let parts = event.deserialize_as::()?; Ok((parts.event_type, parts.content)) } pub(crate) fn raw_room_event_from_parts( event_type: &RoomAccountDataEventType, content: &Raw, ) -> Raw { #[derive(Serialize)] struct Parts<'a> { #[serde(rename = "type")] event_type: &'a RoomAccountDataEventType, content: &'a Raw, } Raw::new(&Parts { event_type, content, }) .expect("json serialization should always succeed") .cast::() } impl Service { pub(crate) fn new(db: &'static dyn Data) -> Self { Self { db, } } /// Places one event in the global account data of the user and removes the /// previous entry. #[tracing::instrument(skip(self, user_id, content))] pub(crate) fn update_global( &self, user_id: &UserId, event_type: &GlobalAccountDataEventType, content: &Raw, ) -> Result<()> { let event = raw_global_event_from_parts(event_type, content); self.db.update(None, user_id, &event_type.to_string(), event.json()) } /// Places one event in the room account data of the user and removes the /// previous entry for that room. #[tracing::instrument(skip(self, room_id, user_id, content))] pub(crate) fn update_room( &self, room_id: &RoomId, user_id: &UserId, event_type: &RoomAccountDataEventType, content: &Raw, ) -> Result<()> { let event = raw_room_event_from_parts(event_type, content); self.db.update( Some(room_id), user_id, &event_type.to_string(), event.json(), ) } /// Searches the global account data for a specific kind. #[tracing::instrument(skip(self, user_id, event_type))] pub(crate) fn get_global( &self, user_id: &UserId, event_type: &GlobalAccountDataEventType, ) -> Result>> { let Some(event) = self.db.get(None, user_id, &event_type.to_string())? else { return Ok(None); }; let event = Raw::::from_json(event); let (_, content) = raw_global_event_to_parts(&event).map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; Ok(Some(content)) } /// Searches the room account data for a specific kind. #[tracing::instrument(skip(self, room_id, user_id, event_type))] pub(crate) fn get_room( &self, room_id: &RoomId, user_id: &UserId, event_type: &RoomAccountDataEventType, ) -> Result>> { let Some(event) = self.db.get(Some(room_id), user_id, &event_type.to_string())? else { return Ok(None); }; let event = Raw::::from_json(event); let (_, content) = raw_room_event_to_parts(&event).map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; Ok(Some(content)) } /// Returns all changes to global account data that happened after `since`. /// /// When there have been multiple changes to the same event type, returned /// map contains the most recent value. #[tracing::instrument(skip(self, user_id, since))] pub(crate) fn global_changes_since( &self, user_id: &UserId, since: u64, ) -> Result< HashMap< GlobalAccountDataEventType, Raw, >, > { self.db .changes_since(None, user_id, since)? .into_values() .map(|event| { let event = Raw::::from_json(event); raw_global_event_to_parts(&event).map_err(|_| { Error::bad_database("Invalid account data event in db") }) }) .collect() } /// Returns all changes to room account data that happened after `since`. /// /// When there have been multiple changes to the same event type, returned /// map contains the most recent value. #[tracing::instrument(skip(self, room_id, user_id, since))] pub(crate) fn room_changes_since( &self, user_id: &UserId, room_id: &RoomId, since: u64, ) -> Result< HashMap>, > { self.db .changes_since(Some(room_id), user_id, since)? .into_values() .map(|event| { let event = Raw::::from_json(event); raw_room_event_to_parts(&event).map_err(|_| { Error::bad_database("Invalid account data event in db") }) }) .collect() } }