ruma_events/
direct.rs
1use std::{
6 collections::{btree_map, BTreeMap},
7 ops::{Deref, DerefMut},
8};
9
10use ruma_common::{IdParseError, OwnedRoomId, OwnedUserId, UserId};
11use ruma_macros::{EventContent, IdZst};
12use serde::{Deserialize, Serialize};
13
14#[repr(transparent)]
20#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
21pub struct DirectUserIdentifier(str);
22
23impl DirectUserIdentifier {
24 pub fn as_user_id(&self) -> Option<&UserId> {
26 self.0.try_into().ok()
27 }
28}
29
30impl OwnedDirectUserIdentifier {
31 pub fn as_user_id(&self) -> Option<&UserId> {
33 self.0.try_into().ok()
34 }
35
36 pub fn into_user_id(self) -> Option<OwnedUserId> {
38 OwnedUserId::try_from(self).ok()
39 }
40}
41
42impl TryFrom<OwnedDirectUserIdentifier> for OwnedUserId {
43 type Error = IdParseError;
44
45 fn try_from(value: OwnedDirectUserIdentifier) -> Result<Self, Self::Error> {
46 value.0.try_into()
47 }
48}
49
50impl TryFrom<&OwnedDirectUserIdentifier> for OwnedUserId {
51 type Error = IdParseError;
52
53 fn try_from(value: &OwnedDirectUserIdentifier) -> Result<Self, Self::Error> {
54 value.0.try_into()
55 }
56}
57
58impl TryFrom<&DirectUserIdentifier> for OwnedUserId {
59 type Error = IdParseError;
60
61 fn try_from(value: &DirectUserIdentifier) -> Result<Self, Self::Error> {
62 value.0.try_into()
63 }
64}
65
66impl<'a> TryFrom<&'a DirectUserIdentifier> for &'a UserId {
67 type Error = IdParseError;
68
69 fn try_from(value: &'a DirectUserIdentifier) -> Result<Self, Self::Error> {
70 value.0.try_into()
71 }
72}
73
74impl From<OwnedUserId> for OwnedDirectUserIdentifier {
75 fn from(value: OwnedUserId) -> Self {
76 DirectUserIdentifier::from_borrowed(value.as_str()).to_owned()
77 }
78}
79
80impl From<&OwnedUserId> for OwnedDirectUserIdentifier {
81 fn from(value: &OwnedUserId) -> Self {
82 DirectUserIdentifier::from_borrowed(value.as_str()).to_owned()
83 }
84}
85
86impl From<&UserId> for OwnedDirectUserIdentifier {
87 fn from(value: &UserId) -> Self {
88 DirectUserIdentifier::from_borrowed(value.as_str()).to_owned()
89 }
90}
91
92impl<'a> From<&'a UserId> for &'a DirectUserIdentifier {
93 fn from(value: &'a UserId) -> Self {
94 DirectUserIdentifier::from_borrowed(value.as_str())
95 }
96}
97
98impl PartialEq<&UserId> for &DirectUserIdentifier {
99 fn eq(&self, other: &&UserId) -> bool {
100 self.0.eq(other.as_str())
101 }
102}
103
104impl PartialEq<&DirectUserIdentifier> for &UserId {
105 fn eq(&self, other: &&DirectUserIdentifier) -> bool {
106 other.0.eq(self.as_str())
107 }
108}
109
110impl PartialEq<OwnedUserId> for &DirectUserIdentifier {
111 fn eq(&self, other: &OwnedUserId) -> bool {
112 self.0.eq(other.as_str())
113 }
114}
115
116impl PartialEq<&DirectUserIdentifier> for OwnedUserId {
117 fn eq(&self, other: &&DirectUserIdentifier) -> bool {
118 other.0.eq(self.as_str())
119 }
120}
121
122impl PartialEq<&UserId> for OwnedDirectUserIdentifier {
123 fn eq(&self, other: &&UserId) -> bool {
124 self.0.eq(other.as_str())
125 }
126}
127
128impl PartialEq<OwnedDirectUserIdentifier> for &UserId {
129 fn eq(&self, other: &OwnedDirectUserIdentifier) -> bool {
130 other.0.eq(self.as_str())
131 }
132}
133
134impl PartialEq<OwnedUserId> for OwnedDirectUserIdentifier {
135 fn eq(&self, other: &OwnedUserId) -> bool {
136 self.0.eq(other.as_str())
137 }
138}
139
140impl PartialEq<OwnedDirectUserIdentifier> for OwnedUserId {
141 fn eq(&self, other: &OwnedDirectUserIdentifier) -> bool {
142 other.0.eq(self.as_str())
143 }
144}
145
146#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
153#[allow(clippy::exhaustive_structs)]
154#[ruma_event(type = "m.direct", kind = GlobalAccountData)]
155pub struct DirectEventContent(pub BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>>);
156
157impl Deref for DirectEventContent {
158 type Target = BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>>;
159
160 fn deref(&self) -> &Self::Target {
161 &self.0
162 }
163}
164
165impl DerefMut for DirectEventContent {
166 fn deref_mut(&mut self) -> &mut Self::Target {
167 &mut self.0
168 }
169}
170
171impl IntoIterator for DirectEventContent {
172 type Item = (OwnedDirectUserIdentifier, Vec<OwnedRoomId>);
173 type IntoIter = btree_map::IntoIter<OwnedDirectUserIdentifier, Vec<OwnedRoomId>>;
174
175 fn into_iter(self) -> Self::IntoIter {
176 self.0.into_iter()
177 }
178}
179
180impl FromIterator<(OwnedDirectUserIdentifier, Vec<OwnedRoomId>)> for DirectEventContent {
181 fn from_iter<T>(iter: T) -> Self
182 where
183 T: IntoIterator<Item = (OwnedDirectUserIdentifier, Vec<OwnedRoomId>)>,
184 {
185 Self(BTreeMap::from_iter(iter))
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use std::collections::BTreeMap;
192
193 use ruma_common::{owned_room_id, user_id, OwnedUserId};
194 use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
195
196 use super::{DirectEvent, DirectEventContent};
197 use crate::direct::{DirectUserIdentifier, OwnedDirectUserIdentifier};
198
199 #[test]
200 fn serialization() {
201 let mut content = DirectEventContent(BTreeMap::new());
202 let alice = user_id!("@alice:ruma.io");
203 let alice_mail = "alice@ruma.io";
204 let rooms = vec![owned_room_id!("!1:ruma.io")];
205 let mail_rooms = vec![owned_room_id!("!3:ruma.io")];
206
207 content.insert(alice.into(), rooms.clone());
208 content.insert(alice_mail.into(), mail_rooms.clone());
209
210 let json_data = json!({
211 alice: rooms,
212 alice_mail: mail_rooms,
213 });
214
215 assert_eq!(to_json_value(&content).unwrap(), json_data);
216 }
217
218 #[test]
219 fn deserialization() {
220 let alice = user_id!("@alice:ruma.io");
221 let alice_mail = "alice@ruma.io";
222 let rooms = vec![owned_room_id!("!1:ruma.io"), owned_room_id!("!2:ruma.io")];
223 let mail_rooms = vec![owned_room_id!("!3:ruma.io")];
224
225 let json_data = json!({
226 "content": {
227 alice: rooms,
228 alice_mail: mail_rooms,
229 },
230 "type": "m.direct"
231 });
232
233 let event: DirectEvent = from_json_value(json_data).unwrap();
234
235 let direct_rooms = event.content.get(<&DirectUserIdentifier>::from(alice)).unwrap();
236 assert!(direct_rooms.contains(&rooms[0]));
237 assert!(direct_rooms.contains(&rooms[1]));
238
239 let email_direct_rooms =
240 event.content.get(<&DirectUserIdentifier>::from(alice_mail)).unwrap();
241 assert!(email_direct_rooms.contains(&mail_rooms[0]));
242 }
243
244 #[test]
245 fn user_id_conversion() {
246 let alice_direct_uid = DirectUserIdentifier::from_borrowed("@alice:ruma.io");
247 let alice_owned_user_id: OwnedUserId = alice_direct_uid
248 .to_owned()
249 .try_into()
250 .expect("@alice:ruma.io should be convertible into a Matrix user ID");
251 assert_eq!(alice_direct_uid, alice_owned_user_id);
252
253 let alice_direct_uid_mail = DirectUserIdentifier::from_borrowed("alice@ruma.io");
254 OwnedUserId::try_from(alice_direct_uid_mail.to_owned())
255 .expect_err("alice@ruma.io should not be convertible into a Matrix user ID");
256
257 let alice_user_id = user_id!("@alice:ruma.io");
258 let alice_direct_uid_mail: &DirectUserIdentifier = alice_user_id.into();
259 assert_eq!(alice_direct_uid_mail, alice_user_id);
260 assert_eq!(alice_direct_uid_mail, alice_user_id.to_owned());
261 assert_eq!(alice_user_id, alice_direct_uid_mail);
262 assert_eq!(alice_user_id.to_owned(), alice_direct_uid_mail);
263
264 let alice_user_id = user_id!("@alice:ruma.io");
265 let alice_direct_uid_mail: OwnedDirectUserIdentifier = alice_user_id.into();
266 assert_eq!(alice_direct_uid_mail, alice_user_id);
267 assert_eq!(alice_direct_uid_mail, alice_user_id.to_owned());
268 assert_eq!(alice_user_id, alice_direct_uid_mail);
269 assert_eq!(alice_user_id.to_owned(), alice_direct_uid_mail);
270
271 let alice_user_id = user_id!("@alice:ruma.io");
272 let alice_user_id_json = to_json_value(alice_user_id).unwrap();
273 let alice_direct_uid_mail: OwnedDirectUserIdentifier =
274 from_json_value(alice_user_id_json).unwrap();
275 assert_eq!(alice_user_id, alice_direct_uid_mail);
276 }
277}