1use std::{collections::BTreeMap, time::Duration};
8
9use js_int::UInt;
10use js_option::JsOption;
11use ruma_common::{
12 api::{request, response, Metadata},
13 directory::RoomTypeFilter,
14 metadata,
15 serde::{deserialize_cow_str, duration::opt_ms, Raw},
16 MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId,
17};
18use ruma_events::{
19 receipt::SyncReceiptEvent, typing::SyncTypingEvent, AnyGlobalAccountDataEvent,
20 AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent, AnySyncTimelineEvent,
21 AnyToDeviceEvent, StateEventType, TimelineEventType,
22};
23use serde::{de::Error as _, Deserialize, Serialize};
24
25#[cfg(feature = "unstable-msc4186")]
26use super::v5;
27use super::{DeviceLists, UnreadNotificationsCount};
28
29const METADATA: Metadata = metadata! {
30 method: POST,
31 rate_limited: false,
32 authentication: AccessToken,
33 history: {
34 unstable => "/_matrix/client/unstable/org.matrix.msc3575/sync",
35 }
37};
38
39#[request(error = crate::Error)]
41#[derive(Default)]
42pub struct Request {
43 #[serde(skip_serializing_if = "Option::is_none")]
48 #[ruma_api(query)]
49 pub pos: Option<String>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
67 pub delta_token: Option<String>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
79 pub conn_id: Option<String>,
80
81 #[serde(skip_serializing_if = "Option::is_none")]
84 pub txn_id: Option<String>,
85
86 #[serde(with = "opt_ms", default, skip_serializing_if = "Option::is_none")]
88 #[ruma_api(query)]
89 pub timeout: Option<Duration>,
90
91 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
94 pub lists: BTreeMap<String, SyncRequestList>,
95
96 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
98 pub room_subscriptions: BTreeMap<OwnedRoomId, RoomSubscription>,
99
100 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
102 pub unsubscribe_rooms: Vec<OwnedRoomId>,
103
104 #[serde(default, skip_serializing_if = "ExtensionsConfig::is_empty")]
106 pub extensions: ExtensionsConfig,
107}
108
109#[response(error = crate::Error)]
111pub struct Response {
112 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
115 pub initial: bool,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub txn_id: Option<String>,
120
121 pub pos: String,
123
124 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
126 pub lists: BTreeMap<String, SyncList>,
127
128 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
130 pub rooms: BTreeMap<OwnedRoomId, SlidingSyncRoom>,
131
132 #[serde(default, skip_serializing_if = "Extensions::is_empty")]
134 pub extensions: Extensions,
135
136 pub delta_token: Option<String>,
152}
153
154impl Request {
155 pub fn new() -> Self {
157 Default::default()
158 }
159}
160
161impl Response {
162 pub fn new(pos: String) -> Self {
164 Self {
165 initial: Default::default(),
166 txn_id: None,
167 pos,
168 delta_token: Default::default(),
169 lists: Default::default(),
170 rooms: Default::default(),
171 extensions: Default::default(),
172 }
173 }
174}
175
176#[derive(Clone, Debug, Default, Serialize, Deserialize)]
185#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
186pub struct SyncRequestListFilters {
187 #[serde(skip_serializing_if = "Option::is_none")]
193 pub is_dm: Option<bool>,
194
195 #[serde(default, skip_serializing_if = "Vec::is_empty")]
203 pub spaces: Vec<String>,
204
205 #[serde(skip_serializing_if = "Option::is_none")]
211 pub is_encrypted: Option<bool>,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
219 pub is_invite: Option<bool>,
220
221 #[serde(skip_serializing_if = "Option::is_none")]
227 pub is_tombstoned: Option<bool>,
228
229 #[serde(default, skip_serializing_if = "Vec::is_empty")]
236 pub room_types: Vec<RoomTypeFilter>,
237
238 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
243 pub not_room_types: Vec<RoomTypeFilter>,
244
245 #[serde(skip_serializing_if = "Option::is_none")]
250 pub room_name_like: Option<String>,
251
252 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
257 pub tags: Vec<String>,
258
259 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
266 pub not_tags: Vec<String>,
267
268 #[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
270 pub extensions: BTreeMap<String, serde_json::Value>,
271}
272
273#[derive(Clone, Debug, Default, Serialize, Deserialize)]
275#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
276pub struct SyncRequestList {
277 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
287 pub slow_get_all_rooms: bool,
288
289 pub ranges: Vec<(UInt, UInt)>,
291
292 #[serde(default, skip_serializing_if = "Vec::is_empty")]
294 pub sort: Vec<String>,
295
296 #[serde(flatten)]
298 pub room_details: RoomDetailsConfig,
299
300 #[serde(skip_serializing_if = "Option::is_none")]
302 pub include_old_rooms: Option<IncludeOldRooms>,
303
304 #[serde(skip_serializing_if = "Option::is_none")]
309 pub include_heroes: Option<bool>,
310
311 #[serde(skip_serializing_if = "Option::is_none")]
313 pub filters: Option<SyncRequestListFilters>,
314
315 #[serde(default, skip_serializing_if = "Vec::is_empty")]
326 pub bump_event_types: Vec<TimelineEventType>,
327}
328
329#[derive(Clone, Debug, Default, Serialize, Deserialize)]
331#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
332pub struct RoomDetailsConfig {
333 #[serde(default, skip_serializing_if = "Vec::is_empty")]
338 pub required_state: Vec<(StateEventType, String)>,
339
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub timeline_limit: Option<UInt>,
343}
344
345#[derive(Clone, Debug, Default, Serialize, Deserialize)]
347#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
348pub struct IncludeOldRooms {
349 #[serde(default, skip_serializing_if = "Vec::is_empty")]
354 pub required_state: Vec<(StateEventType, String)>,
355
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub timeline_limit: Option<UInt>,
359}
360
361#[derive(Clone, Debug, Default, Serialize, Deserialize)]
363#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
364pub struct RoomSubscription {
365 #[serde(default, skip_serializing_if = "Vec::is_empty")]
370 pub required_state: Vec<(StateEventType, String)>,
371
372 #[serde(skip_serializing_if = "Option::is_none")]
374 pub timeline_limit: Option<UInt>,
375
376 #[serde(skip_serializing_if = "Option::is_none")]
378 pub include_heroes: Option<bool>,
379}
380
381#[derive(Clone, Debug, Deserialize, Serialize)]
383#[serde(rename_all = "UPPERCASE")]
384#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
385pub enum SlidingOp {
386 Sync,
388 Insert,
391 Delete,
393 Invalidate,
395}
396
397#[derive(Clone, Debug, Default, Deserialize, Serialize)]
399#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
400pub struct SyncList {
401 #[serde(default, skip_serializing_if = "Vec::is_empty")]
403 pub ops: Vec<SyncOp>,
404
405 pub count: UInt,
407}
408
409#[derive(Clone, Debug, Deserialize, Serialize)]
411#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
412pub struct SyncOp {
413 pub op: SlidingOp,
415
416 pub range: Option<(UInt, UInt)>,
418
419 pub index: Option<UInt>,
421
422 #[serde(default, skip_serializing_if = "Vec::is_empty")]
424 pub room_ids: Vec<OwnedRoomId>,
425
426 pub room_id: Option<OwnedRoomId>,
428}
429
430#[derive(Clone, Debug, Default, Deserialize, Serialize)]
432#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
433pub struct SlidingSyncRoom {
434 #[serde(skip_serializing_if = "Option::is_none")]
436 pub name: Option<String>,
437
438 #[serde(default, skip_serializing_if = "JsOption::is_undefined")]
440 pub avatar: JsOption<OwnedMxcUri>,
441
442 #[serde(skip_serializing_if = "Option::is_none")]
444 pub initial: Option<bool>,
445
446 #[serde(skip_serializing_if = "Option::is_none")]
448 pub is_dm: Option<bool>,
449
450 #[serde(skip_serializing_if = "Option::is_none")]
453 pub invite_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
454
455 #[serde(flatten, default, skip_serializing_if = "UnreadNotificationsCount::is_empty")]
457 pub unread_notifications: UnreadNotificationsCount,
458
459 #[serde(default, skip_serializing_if = "Vec::is_empty")]
461 pub timeline: Vec<Raw<AnySyncTimelineEvent>>,
462
463 #[serde(default, skip_serializing_if = "Vec::is_empty")]
466 pub required_state: Vec<Raw<AnySyncStateEvent>>,
467
468 #[serde(skip_serializing_if = "Option::is_none")]
470 pub prev_batch: Option<String>,
471
472 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
474 pub limited: bool,
475
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub joined_count: Option<UInt>,
479
480 #[serde(skip_serializing_if = "Option::is_none")]
482 pub invited_count: Option<UInt>,
483
484 #[serde(skip_serializing_if = "Option::is_none")]
486 pub num_live: Option<UInt>,
487
488 #[serde(skip_serializing_if = "Option::is_none")]
495 pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
496
497 #[serde(skip_serializing_if = "Option::is_none")]
499 pub heroes: Option<Vec<SlidingSyncRoomHero>>,
500}
501
502impl SlidingSyncRoom {
503 pub fn new() -> Self {
505 Default::default()
506 }
507}
508
509#[derive(Clone, Debug, Deserialize, Serialize)]
511#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
512pub struct SlidingSyncRoomHero {
513 pub user_id: OwnedUserId,
515
516 #[serde(rename = "displayname", skip_serializing_if = "Option::is_none")]
518 pub name: Option<String>,
519
520 #[serde(rename = "avatar_url", skip_serializing_if = "Option::is_none")]
522 pub avatar: Option<OwnedMxcUri>,
523}
524
525impl SlidingSyncRoomHero {
526 pub fn new(user_id: OwnedUserId) -> Self {
528 Self { user_id, name: None, avatar: None }
529 }
530}
531
532#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
534#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
535pub struct ExtensionsConfig {
536 #[serde(default, skip_serializing_if = "ToDeviceConfig::is_empty")]
538 pub to_device: ToDeviceConfig,
539
540 #[serde(default, skip_serializing_if = "E2EEConfig::is_empty")]
542 pub e2ee: E2EEConfig,
543
544 #[serde(default, skip_serializing_if = "AccountDataConfig::is_empty")]
546 pub account_data: AccountDataConfig,
547
548 #[serde(default, skip_serializing_if = "ReceiptsConfig::is_empty")]
550 pub receipts: ReceiptsConfig,
551
552 #[serde(default, skip_serializing_if = "TypingConfig::is_empty")]
554 pub typing: TypingConfig,
555
556 #[serde(flatten)]
558 other: BTreeMap<String, serde_json::Value>,
559}
560
561impl ExtensionsConfig {
562 pub fn is_empty(&self) -> bool {
564 self.to_device.is_empty()
565 && self.e2ee.is_empty()
566 && self.account_data.is_empty()
567 && self.receipts.is_empty()
568 && self.typing.is_empty()
569 && self.other.is_empty()
570 }
571}
572
573#[derive(Clone, Debug, Default, Serialize, Deserialize)]
575#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
576pub struct Extensions {
577 #[serde(skip_serializing_if = "Option::is_none")]
579 pub to_device: Option<ToDevice>,
580
581 #[serde(default, skip_serializing_if = "E2EE::is_empty")]
583 pub e2ee: E2EE,
584
585 #[serde(default, skip_serializing_if = "AccountData::is_empty")]
587 pub account_data: AccountData,
588
589 #[serde(default, skip_serializing_if = "Receipts::is_empty")]
591 pub receipts: Receipts,
592
593 #[serde(default, skip_serializing_if = "Typing::is_empty")]
595 pub typing: Typing,
596}
597
598impl Extensions {
599 pub fn is_empty(&self) -> bool {
603 self.to_device.is_none()
604 && self.e2ee.is_empty()
605 && self.account_data.is_empty()
606 && self.receipts.is_empty()
607 && self.typing.is_empty()
608 }
609}
610
611#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
615#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
616pub struct ToDeviceConfig {
617 #[serde(skip_serializing_if = "Option::is_none")]
619 pub enabled: Option<bool>,
620
621 #[serde(skip_serializing_if = "Option::is_none")]
623 pub limit: Option<UInt>,
624
625 #[serde(skip_serializing_if = "Option::is_none")]
627 pub since: Option<String>,
628
629 #[serde(skip_serializing_if = "Option::is_none")]
636 pub lists: Option<Vec<String>>,
637
638 #[serde(skip_serializing_if = "Option::is_none")]
645 pub rooms: Option<Vec<OwnedRoomId>>,
646}
647
648impl ToDeviceConfig {
649 pub fn is_empty(&self) -> bool {
651 self.enabled.is_none() && self.limit.is_none() && self.since.is_none()
652 }
653}
654
655#[derive(Clone, Debug, Default, Serialize, Deserialize)]
659#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
660pub struct ToDevice {
661 pub next_batch: String,
663
664 #[serde(default, skip_serializing_if = "Vec::is_empty")]
666 pub events: Vec<Raw<AnyToDeviceEvent>>,
667}
668
669#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
673#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
674pub struct E2EEConfig {
675 #[serde(skip_serializing_if = "Option::is_none")]
677 pub enabled: Option<bool>,
678}
679
680impl E2EEConfig {
681 pub fn is_empty(&self) -> bool {
683 self.enabled.is_none()
684 }
685}
686
687#[derive(Clone, Debug, Default, Serialize, Deserialize)]
691#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
692pub struct E2EE {
693 #[serde(default, skip_serializing_if = "DeviceLists::is_empty")]
697 pub device_lists: DeviceLists,
698
699 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
702 pub device_one_time_keys_count: BTreeMap<OneTimeKeyAlgorithm, UInt>,
703
704 #[serde(skip_serializing_if = "Option::is_none")]
709 pub device_unused_fallback_key_types: Option<Vec<OneTimeKeyAlgorithm>>,
710}
711
712impl E2EE {
713 pub fn is_empty(&self) -> bool {
715 self.device_lists.is_empty()
716 && self.device_one_time_keys_count.is_empty()
717 && self.device_unused_fallback_key_types.is_none()
718 }
719}
720
721#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
726#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
727pub struct AccountDataConfig {
728 #[serde(skip_serializing_if = "Option::is_none")]
730 pub enabled: Option<bool>,
731
732 #[serde(skip_serializing_if = "Option::is_none")]
741 pub lists: Option<Vec<String>>,
742
743 #[serde(skip_serializing_if = "Option::is_none")]
752 pub rooms: Option<Vec<OwnedRoomId>>,
753}
754
755impl AccountDataConfig {
756 pub fn is_empty(&self) -> bool {
758 self.enabled.is_none()
759 }
760}
761
762#[derive(Clone, Debug, Default, Serialize, Deserialize)]
767#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
768pub struct AccountData {
769 #[serde(default, skip_serializing_if = "Vec::is_empty")]
771 pub global: Vec<Raw<AnyGlobalAccountDataEvent>>,
772
773 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
775 pub rooms: BTreeMap<OwnedRoomId, Vec<Raw<AnyRoomAccountDataEvent>>>,
776}
777
778impl AccountData {
779 pub fn is_empty(&self) -> bool {
781 self.global.is_empty() && self.rooms.is_empty()
782 }
783}
784
785#[derive(Clone, Debug, PartialEq)]
787#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
788pub enum RoomReceiptConfig {
789 AllSubscribed,
791 Room(OwnedRoomId),
793}
794
795impl Serialize for RoomReceiptConfig {
796 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
797 where
798 S: serde::Serializer,
799 {
800 match self {
801 RoomReceiptConfig::AllSubscribed => serializer.serialize_str("*"),
802 RoomReceiptConfig::Room(r) => r.serialize(serializer),
803 }
804 }
805}
806
807impl<'de> Deserialize<'de> for RoomReceiptConfig {
808 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
809 where
810 D: serde::de::Deserializer<'de>,
811 {
812 match deserialize_cow_str(deserializer)?.as_ref() {
813 "*" => Ok(RoomReceiptConfig::AllSubscribed),
814 other => Ok(RoomReceiptConfig::Room(
815 RoomId::parse(other).map_err(D::Error::custom)?.to_owned(),
816 )),
817 }
818 }
819}
820
821#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
825#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
826pub struct ReceiptsConfig {
827 #[serde(skip_serializing_if = "Option::is_none")]
829 pub enabled: Option<bool>,
830
831 #[serde(skip_serializing_if = "Option::is_none")]
838 pub lists: Option<Vec<String>>,
839
840 #[serde(skip_serializing_if = "Option::is_none")]
847 pub rooms: Option<Vec<RoomReceiptConfig>>,
848}
849
850impl ReceiptsConfig {
851 pub fn is_empty(&self) -> bool {
853 self.enabled.is_none()
854 }
855}
856
857#[derive(Clone, Debug, Default, Serialize, Deserialize)]
861#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
862pub struct Receipts {
863 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
865 pub rooms: BTreeMap<OwnedRoomId, Raw<SyncReceiptEvent>>,
866}
867
868impl Receipts {
869 pub fn is_empty(&self) -> bool {
871 self.rooms.is_empty()
872 }
873}
874
875#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
880#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
881pub struct TypingConfig {
882 #[serde(skip_serializing_if = "Option::is_none")]
884 pub enabled: Option<bool>,
885
886 #[serde(skip_serializing_if = "Option::is_none")]
893 pub lists: Option<Vec<String>>,
894
895 #[serde(skip_serializing_if = "Option::is_none")]
902 pub rooms: Option<Vec<OwnedRoomId>>,
903}
904
905impl TypingConfig {
906 pub fn is_empty(&self) -> bool {
908 self.enabled.is_none()
909 }
910}
911
912#[derive(Clone, Debug, Default, Serialize, Deserialize)]
917#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
918pub struct Typing {
919 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
921 pub rooms: BTreeMap<OwnedRoomId, Raw<SyncTypingEvent>>,
922}
923
924impl Typing {
925 pub fn is_empty(&self) -> bool {
927 self.rooms.is_empty()
928 }
929}
930
931#[cfg(feature = "unstable-msc4186")]
932impl From<v5::Request> for Request {
933 fn from(value: v5::Request) -> Self {
934 Self {
935 pos: value.pos,
936 conn_id: value.conn_id,
937 txn_id: value.txn_id,
938 timeout: value.timeout,
939 lists: value
940 .lists
941 .into_iter()
942 .map(|(list_name, list)| (list_name, list.into()))
943 .collect(),
944 room_subscriptions: value
945 .room_subscriptions
946 .into_iter()
947 .map(|(room_id, room_subscription)| (room_id, room_subscription.into()))
948 .collect(),
949 extensions: value.extensions.into(),
950
951 ..Default::default()
952 }
953 }
954}
955
956#[cfg(feature = "unstable-msc4186")]
957impl From<v5::request::List> for SyncRequestList {
958 fn from(value: v5::request::List) -> Self {
959 Self {
960 ranges: value.ranges,
961 room_details: value.room_details.into(),
962 include_heroes: value.include_heroes,
963 filters: value.filters.map(Into::into),
964
965 sort: vec!["by_recency".to_owned(), "by_name".to_owned()],
967 bump_event_types: vec![
968 TimelineEventType::RoomMessage,
969 TimelineEventType::RoomEncrypted,
970 TimelineEventType::RoomCreate,
971 TimelineEventType::Sticker,
972 ],
973
974 ..Default::default()
975 }
976 }
977}
978
979#[cfg(feature = "unstable-msc4186")]
980impl From<v5::request::RoomDetails> for RoomDetailsConfig {
981 fn from(value: v5::request::RoomDetails) -> Self {
982 Self { required_state: value.required_state, timeline_limit: Some(value.timeline_limit) }
983 }
984}
985
986#[cfg(feature = "unstable-msc4186")]
987impl From<v5::request::ListFilters> for SyncRequestListFilters {
988 fn from(value: v5::request::ListFilters) -> Self {
989 Self {
990 is_invite: value.is_invite,
991 not_room_types: value.not_room_types,
992 ..Default::default()
993 }
994 }
995}
996
997#[cfg(feature = "unstable-msc4186")]
998impl From<v5::request::RoomSubscription> for RoomSubscription {
999 fn from(value: v5::request::RoomSubscription) -> Self {
1000 Self {
1001 required_state: value.required_state,
1002 timeline_limit: Some(value.timeline_limit),
1003 include_heroes: value.include_heroes,
1004 }
1005 }
1006}
1007
1008#[cfg(feature = "unstable-msc4186")]
1009impl From<v5::request::Extensions> for ExtensionsConfig {
1010 fn from(value: v5::request::Extensions) -> Self {
1011 Self {
1012 to_device: value.to_device.into(),
1013 e2ee: value.e2ee.into(),
1014 account_data: value.account_data.into(),
1015 receipts: value.receipts.into(),
1016 typing: value.typing.into(),
1017
1018 ..Default::default()
1019 }
1020 }
1021}
1022
1023#[cfg(feature = "unstable-msc4186")]
1024impl From<v5::request::ToDevice> for ToDeviceConfig {
1025 fn from(value: v5::request::ToDevice) -> Self {
1026 Self {
1027 enabled: value.enabled,
1028 limit: value.limit,
1029 since: value.since,
1030 lists: value.lists,
1031 rooms: value.rooms,
1032 }
1033 }
1034}
1035
1036#[cfg(feature = "unstable-msc4186")]
1037impl From<v5::request::E2EE> for E2EEConfig {
1038 fn from(value: v5::request::E2EE) -> Self {
1039 Self { enabled: value.enabled }
1040 }
1041}
1042
1043#[cfg(feature = "unstable-msc4186")]
1044impl From<v5::request::AccountData> for AccountDataConfig {
1045 fn from(value: v5::request::AccountData) -> Self {
1046 Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms }
1047 }
1048}
1049
1050#[cfg(feature = "unstable-msc4186")]
1051impl From<v5::request::Receipts> for ReceiptsConfig {
1052 fn from(value: v5::request::Receipts) -> Self {
1053 Self {
1054 enabled: value.enabled,
1055 lists: value.lists,
1056 rooms: value.rooms.map(|rooms| rooms.into_iter().map(Into::into).collect()),
1057 }
1058 }
1059}
1060
1061#[cfg(feature = "unstable-msc4186")]
1062impl From<v5::request::ReceiptsRoom> for RoomReceiptConfig {
1063 fn from(value: v5::request::ReceiptsRoom) -> Self {
1064 match value {
1065 v5::request::ReceiptsRoom::Room(room_id) => Self::Room(room_id),
1066 _ => Self::AllSubscribed,
1067 }
1068 }
1069}
1070
1071#[cfg(feature = "unstable-msc4186")]
1072impl From<v5::request::Typing> for TypingConfig {
1073 fn from(value: v5::request::Typing) -> Self {
1074 Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms }
1075 }
1076}
1077
1078#[cfg(test)]
1079mod tests {
1080 use ruma_common::owned_room_id;
1081
1082 use crate::sync::sync_events::v4::RoomReceiptConfig;
1083
1084 #[test]
1085 fn serialize_room_receipt_config() {
1086 let entry = RoomReceiptConfig::AllSubscribed;
1087 assert_eq!(serde_json::to_string(&entry).unwrap().as_str(), r#""*""#);
1088
1089 let entry = RoomReceiptConfig::Room(owned_room_id!("!n8f893n9:example.com"));
1090 assert_eq!(serde_json::to_string(&entry).unwrap().as_str(), r#""!n8f893n9:example.com""#);
1091 }
1092
1093 #[test]
1094 fn deserialize_room_receipt_config() {
1095 assert_eq!(
1096 serde_json::from_str::<RoomReceiptConfig>(r#""*""#).unwrap(),
1097 RoomReceiptConfig::AllSubscribed
1098 );
1099
1100 assert_eq!(
1101 serde_json::from_str::<RoomReceiptConfig>(r#""!n8f893n9:example.com""#).unwrap(),
1102 RoomReceiptConfig::Room(owned_room_id!("!n8f893n9:example.com"))
1103 );
1104 }
1105}