use std::{collections::BTreeMap, time::Duration};
use salvo::prelude::*;
use serde::{Deserialize, Serialize, de::Error as _};
use crate::device::DeviceLists;
use crate::directory::RoomTypeFilter;
use crate::events::receipt::SyncReceiptEvent;
use crate::events::typing::SyncTypingEvent;
use crate::events::{
AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent, AnySyncTimelineEvent,
AnyToDeviceEvent, StateEventType, TimelineEventType,
};
use crate::serde::{RawJson, deserialize_cow_str, duration::opt_ms};
use crate::{DeviceKeyAlgorithm, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, UnixMillis};
use super::UnreadNotificationsCount;
#[derive(ToParameters, Deserialize, Debug)]
pub struct SyncEventsReqArgs {
#[salvo(parameter(parameter_in = Query))]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pos: Option<String>,
#[salvo(parameter(parameter_in = Query))]
#[serde(with = "opt_ms", default, skip_serializing_if = "Option::is_none")]
pub timeout: Option<Duration>,
}
#[derive(ToSchema, Deserialize, Debug)]
pub struct SyncEventsReqBody {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delta_token: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub conn_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub txn_id: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub lists: BTreeMap<String, ReqList>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub room_subscriptions: BTreeMap<OwnedRoomId, RoomSubscription>,
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub unsubscribe_rooms: Vec<OwnedRoomId>,
#[serde(default, skip_serializing_if = "ExtensionsConfig::is_empty")]
pub extensions: ExtensionsConfig,
}
#[derive(ToSchema, Serialize, Default, Debug)]
pub struct SyncEventsResBody {
#[serde(default, skip_serializing_if = "crate::serde::is_default")]
pub initial: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub txn_id: Option<String>,
pub pos: String,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub lists: BTreeMap<String, SyncList>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub rooms: BTreeMap<OwnedRoomId, SyncRoom>,
#[serde(default, skip_serializing_if = "Extensions::is_empty")]
pub extensions: Extensions,
pub delta_token: Option<String>,
}
impl SyncEventsResBody {
pub fn new(pos: String) -> Self {
Self {
pos,
..Default::default()
}
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct ReqListFilters {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_dm: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub spaces: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_encrypted: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_invite: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_tombstoned: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub room_types: Vec<RoomTypeFilter>,
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub not_room_types: Vec<RoomTypeFilter>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub room_name_like: Option<String>,
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub not_tags: Vec<String>,
#[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
#[salvo(schema(value_type = Object))]
pub extensions: BTreeMap<String, serde_json::Value>,
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct ReqList {
#[serde(default, skip_serializing_if = "crate::serde::is_default")]
pub slow_get_all_rooms: bool,
pub ranges: Vec<(u64, u64)>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub sort: Vec<String>,
#[serde(flatten)]
pub room_details: RoomDetailsConfig,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub include_old_rooms: Option<IncludeOldRooms>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filters: Option<ReqListFilters>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub bump_event_types: Vec<TimelineEventType>,
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct RoomDetailsConfig {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub required_state: Vec<(StateEventType, String)>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeline_limit: Option<usize>,
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct IncludeOldRooms {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub required_state: Vec<(StateEventType, String)>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeline_limit: Option<usize>,
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct RoomSubscription {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub required_state: Vec<(StateEventType, String)>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeline_limit: Option<usize>,
}
#[derive(ToSchema, Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub enum SlidingOp {
Sync,
Insert,
Delete,
Invalidate,
}
#[derive(ToSchema, Deserialize, Serialize, Clone, Debug)]
pub struct SyncList {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub ops: Vec<SyncOp>,
pub count: u64,
}
#[derive(ToSchema, Deserialize, Serialize, Clone, Debug)]
pub struct SyncOp {
pub op: SlidingOp,
pub range: Option<(u64, u64)>,
pub index: Option<u64>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub room_ids: Vec<OwnedRoomId>,
pub room_id: Option<OwnedRoomId>,
}
#[derive(ToSchema, Clone, Debug, Default, Deserialize, Serialize)]
pub struct SyncRoom {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub avatar: Option<OwnedMxcUri>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub initial: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_dm: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub invite_state: Option<Vec<RawJson<AnyStrippedStateEvent>>>,
#[serde(flatten, default, skip_serializing_if = "UnreadNotificationsCount::is_empty")]
pub unread_notifications: UnreadNotificationsCount,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub timeline: Vec<RawJson<AnySyncTimelineEvent>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub required_state: Vec<RawJson<AnySyncStateEvent>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub prev_batch: Option<String>,
#[serde(default, skip_serializing_if = "crate::serde::is_default")]
pub limited: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub joined_count: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub invited_count: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub num_live: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timestamp: Option<UnixMillis>,
#[serde(skip_serializing_if = "Option::is_none")]
pub heroes: Option<Vec<SyncRoomHero>>,
}
impl SyncRoom {
pub fn new() -> Self {
Default::default()
}
}
#[derive(ToSchema, Clone, Debug, Deserialize, Serialize)]
pub struct SyncRoomHero {
pub user_id: OwnedUserId,
#[serde(rename = "displayname", skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(rename = "avatar_url", skip_serializing_if = "Option::is_none")]
pub avatar: Option<OwnedMxcUri>,
}
impl SyncRoomHero {
pub fn new(user_id: OwnedUserId) -> Self {
Self {
user_id,
name: None,
avatar: None,
}
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct ExtensionsConfig {
#[serde(default, skip_serializing_if = "ToDeviceConfig::is_empty")]
pub to_device: ToDeviceConfig,
#[serde(default, skip_serializing_if = "E2eeConfig::is_empty")]
pub e2ee: E2eeConfig,
#[serde(default, skip_serializing_if = "AccountDataConfig::is_empty")]
pub account_data: AccountDataConfig,
#[serde(default, skip_serializing_if = "ReceiptsConfig::is_empty")]
pub receipts: ReceiptsConfig,
#[serde(default, skip_serializing_if = "TypingConfig::is_empty")]
pub typing: TypingConfig,
#[serde(flatten)]
#[salvo(schema(value_type = Object, additional_properties = true))]
other: BTreeMap<String, serde_json::Value>,
}
impl ExtensionsConfig {
pub fn is_empty(&self) -> bool {
self.to_device.is_empty()
&& self.e2ee.is_empty()
&& self.account_data.is_empty()
&& self.receipts.is_empty()
&& self.typing.is_empty()
&& self.other.is_empty()
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct Extensions {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub to_device: Option<ToDevice>,
#[serde(default, skip_serializing_if = "E2ee::is_empty")]
pub e2ee: E2ee,
#[serde(default, skip_serializing_if = "AccountData::is_empty")]
pub account_data: AccountData,
#[serde(default, skip_serializing_if = "Receipts::is_empty")]
pub receipts: Receipts,
#[serde(default, skip_serializing_if = "Typing::is_empty")]
pub typing: Typing,
}
impl Extensions {
pub fn is_empty(&self) -> bool {
self.to_device.is_none()
&& self.e2ee.is_empty()
&& self.account_data.is_empty()
&& self.receipts.is_empty()
&& self.typing.is_empty()
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct ToDeviceConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limit: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub since: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lists: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rooms: Option<Vec<OwnedRoomId>>,
}
impl ToDeviceConfig {
pub fn is_empty(&self) -> bool {
self.enabled.is_none() && self.limit.is_none() && self.since.is_none()
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct ToDevice {
pub next_batch: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub events: Vec<RawJson<AnyToDeviceEvent>>,
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct E2eeConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
}
impl E2eeConfig {
pub fn is_empty(&self) -> bool {
self.enabled.is_none()
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct E2ee {
#[serde(default, skip_serializing_if = "DeviceLists::is_empty")]
pub device_lists: DeviceLists,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub device_one_time_keys_count: BTreeMap<DeviceKeyAlgorithm, u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub device_unused_fallback_key_types: Option<Vec<DeviceKeyAlgorithm>>,
}
impl E2ee {
pub fn is_empty(&self) -> bool {
self.device_lists.is_empty()
&& self.device_one_time_keys_count.is_empty()
&& self.device_unused_fallback_key_types.is_none()
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct AccountDataConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lists: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rooms: Option<Vec<OwnedRoomId>>,
}
impl AccountDataConfig {
pub fn is_empty(&self) -> bool {
self.enabled.is_none()
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct AccountData {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub global: Vec<RawJson<AnyGlobalAccountDataEvent>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub rooms: BTreeMap<OwnedRoomId, Vec<RawJson<AnyRoomAccountDataEvent>>>,
}
impl AccountData {
pub fn is_empty(&self) -> bool {
self.global.is_empty() && self.rooms.is_empty()
}
}
#[derive(ToSchema, Clone, Debug, PartialEq)]
pub enum RoomReceiptConfig {
AllSubscribed,
Room(OwnedRoomId),
}
impl Serialize for RoomReceiptConfig {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
RoomReceiptConfig::AllSubscribed => serializer.serialize_str("*"),
RoomReceiptConfig::Room(r) => r.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for RoomReceiptConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
match deserialize_cow_str(deserializer)?.as_ref() {
"*" => Ok(RoomReceiptConfig::AllSubscribed),
other => Ok(RoomReceiptConfig::Room(
RoomId::parse(other).map_err(D::Error::custom)?.to_owned(),
)),
}
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct ReceiptsConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lists: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rooms: Option<Vec<RoomReceiptConfig>>,
}
impl ReceiptsConfig {
pub fn is_empty(&self) -> bool {
self.enabled.is_none()
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct Receipts {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[salvo(schema(value_type = Object, additional_properties = true))]
pub rooms: BTreeMap<OwnedRoomId, RawJson<SyncReceiptEvent>>,
}
impl Receipts {
pub fn is_empty(&self) -> bool {
self.rooms.is_empty()
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct TypingConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lists: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rooms: Option<Vec<OwnedRoomId>>,
}
impl TypingConfig {
pub fn is_empty(&self) -> bool {
self.enabled.is_none()
}
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct Typing {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[salvo(schema(value_type = Object, additional_properties = true))]
pub rooms: BTreeMap<OwnedRoomId, RawJson<SyncTypingEvent>>,
}
impl Typing {
pub fn is_empty(&self) -> bool {
self.rooms.is_empty()
}
}