forked from ruma/ruma
-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ruma-events: Accept any string as a key for
m.direct
account data
- Loading branch information
1 parent
8cade7a
commit 2ab432f
Showing
2 changed files
with
197 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,23 +7,155 @@ use std::{ | |
ops::{Deref, DerefMut}, | ||
}; | ||
|
||
use ruma_common::{OwnedRoomId, OwnedUserId}; | ||
use ruma_macros::EventContent; | ||
use ruma_common::{IdParseError, OwnedRoomId, OwnedUserId, UserId}; | ||
use ruma_macros::{EventContent, IdZst}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
/// An user identifier, it can be a [`UserId`] or a third-party identifier | ||
/// like an email or a phone number. | ||
/// | ||
/// There is no validation on this type, any string is allowed, | ||
/// but you can use `as_user_id` or `into_user_id` to try to get an [`UserId`]. | ||
#[repr(transparent)] | ||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)] | ||
pub struct DirectUserIdentifier(str); | ||
|
||
impl DirectUserIdentifier { | ||
/// Get this `DirectUserIdentifier` as an [`UserId`] if it is one. | ||
pub fn as_user_id(&self) -> Option<&UserId> { | ||
self.0.try_into().ok() | ||
} | ||
} | ||
|
||
impl OwnedDirectUserIdentifier { | ||
/// Get this `OwnedDirectUserIdentifier` as an [`UserId`] if it is one. | ||
pub fn as_user_id(&self) -> Option<&UserId> { | ||
self.0.try_into().ok() | ||
} | ||
|
||
/// Get this `OwnedDirectUserIdentifier` as an [`OwnedUserId`] if it is one. | ||
pub fn into_user_id(self) -> Option<OwnedUserId> { | ||
OwnedUserId::try_from(self).ok() | ||
} | ||
} | ||
|
||
impl TryFrom<OwnedDirectUserIdentifier> for OwnedUserId { | ||
type Error = IdParseError; | ||
|
||
fn try_from(value: OwnedDirectUserIdentifier) -> Result<Self, Self::Error> { | ||
value.0.try_into() | ||
} | ||
} | ||
|
||
impl TryFrom<&OwnedDirectUserIdentifier> for OwnedUserId { | ||
type Error = IdParseError; | ||
|
||
fn try_from(value: &OwnedDirectUserIdentifier) -> Result<Self, Self::Error> { | ||
value.0.try_into() | ||
} | ||
} | ||
|
||
impl TryFrom<&DirectUserIdentifier> for OwnedUserId { | ||
type Error = IdParseError; | ||
|
||
fn try_from(value: &DirectUserIdentifier) -> Result<Self, Self::Error> { | ||
value.0.try_into() | ||
} | ||
} | ||
|
||
impl<'a> TryFrom<&'a DirectUserIdentifier> for &'a UserId { | ||
type Error = IdParseError; | ||
|
||
fn try_from(value: &'a DirectUserIdentifier) -> Result<Self, Self::Error> { | ||
value.0.try_into() | ||
} | ||
} | ||
|
||
impl From<OwnedUserId> for OwnedDirectUserIdentifier { | ||
fn from(value: OwnedUserId) -> Self { | ||
DirectUserIdentifier::from_borrowed(value.as_str()).to_owned() | ||
} | ||
} | ||
|
||
impl From<&OwnedUserId> for OwnedDirectUserIdentifier { | ||
fn from(value: &OwnedUserId) -> Self { | ||
DirectUserIdentifier::from_borrowed(value.as_str()).to_owned() | ||
} | ||
} | ||
|
||
impl From<&UserId> for OwnedDirectUserIdentifier { | ||
fn from(value: &UserId) -> Self { | ||
DirectUserIdentifier::from_borrowed(value.as_str()).to_owned() | ||
} | ||
} | ||
|
||
impl<'a> From<&'a UserId> for &'a DirectUserIdentifier { | ||
fn from(value: &'a UserId) -> Self { | ||
DirectUserIdentifier::from_borrowed(value.as_str()) | ||
} | ||
} | ||
|
||
impl PartialEq<&UserId> for &DirectUserIdentifier { | ||
fn eq(&self, other: &&UserId) -> bool { | ||
self.0.eq(other.as_str()) | ||
} | ||
} | ||
|
||
impl PartialEq<&DirectUserIdentifier> for &UserId { | ||
fn eq(&self, other: &&DirectUserIdentifier) -> bool { | ||
other.0.eq(self.as_str()) | ||
} | ||
} | ||
|
||
impl PartialEq<OwnedUserId> for &DirectUserIdentifier { | ||
fn eq(&self, other: &OwnedUserId) -> bool { | ||
self.0.eq(other.as_str()) | ||
} | ||
} | ||
|
||
impl PartialEq<&DirectUserIdentifier> for OwnedUserId { | ||
fn eq(&self, other: &&DirectUserIdentifier) -> bool { | ||
other.0.eq(self.as_str()) | ||
} | ||
} | ||
|
||
impl PartialEq<&UserId> for OwnedDirectUserIdentifier { | ||
fn eq(&self, other: &&UserId) -> bool { | ||
self.0.eq(other.as_str()) | ||
} | ||
} | ||
|
||
impl PartialEq<OwnedDirectUserIdentifier> for &UserId { | ||
fn eq(&self, other: &OwnedDirectUserIdentifier) -> bool { | ||
other.0.eq(self.as_str()) | ||
} | ||
} | ||
|
||
impl PartialEq<OwnedUserId> for OwnedDirectUserIdentifier { | ||
fn eq(&self, other: &OwnedUserId) -> bool { | ||
self.0.eq(other.as_str()) | ||
} | ||
} | ||
|
||
impl PartialEq<OwnedDirectUserIdentifier> for OwnedUserId { | ||
fn eq(&self, other: &OwnedDirectUserIdentifier) -> bool { | ||
other.0.eq(self.as_str()) | ||
} | ||
} | ||
|
||
/// The content of an `m.direct` event. | ||
/// | ||
/// A mapping of `UserId`s to a list of `RoomId`s which are considered *direct* for that particular | ||
/// user. | ||
/// A mapping of `DirectUserIdentifier`s to a list of `RoomId`s which are considered *direct* | ||
/// for that particular user. | ||
/// | ||
/// Informs the client about the rooms that are considered direct by a user. | ||
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)] | ||
#[allow(clippy::exhaustive_structs)] | ||
#[ruma_event(type = "m.direct", kind = GlobalAccountData)] | ||
pub struct DirectEventContent(pub BTreeMap<OwnedUserId, Vec<OwnedRoomId>>); | ||
pub struct DirectEventContent(pub BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>>); | ||
|
||
impl Deref for DirectEventContent { | ||
type Target = BTreeMap<OwnedUserId, Vec<OwnedRoomId>>; | ||
type Target = BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
|
@@ -37,18 +169,18 @@ impl DerefMut for DirectEventContent { | |
} | ||
|
||
impl IntoIterator for DirectEventContent { | ||
type Item = (OwnedUserId, Vec<OwnedRoomId>); | ||
type IntoIter = btree_map::IntoIter<OwnedUserId, Vec<OwnedRoomId>>; | ||
type Item = (OwnedDirectUserIdentifier, Vec<OwnedRoomId>); | ||
type IntoIter = btree_map::IntoIter<OwnedDirectUserIdentifier, Vec<OwnedRoomId>>; | ||
|
||
fn into_iter(self) -> Self::IntoIter { | ||
self.0.into_iter() | ||
} | ||
} | ||
|
||
impl FromIterator<(OwnedUserId, Vec<OwnedRoomId>)> for DirectEventContent { | ||
impl FromIterator<(OwnedDirectUserIdentifier, Vec<OwnedRoomId>)> for DirectEventContent { | ||
fn from_iter<T>(iter: T) -> Self | ||
where | ||
T: IntoIterator<Item = (OwnedUserId, Vec<OwnedRoomId>)>, | ||
T: IntoIterator<Item = (OwnedDirectUserIdentifier, Vec<OwnedRoomId>)>, | ||
{ | ||
Self(BTreeMap::from_iter(iter)) | ||
} | ||
|
@@ -58,42 +190,82 @@ impl FromIterator<(OwnedUserId, Vec<OwnedRoomId>)> for DirectEventContent { | |
mod tests { | ||
use std::collections::BTreeMap; | ||
|
||
use ruma_common::{owned_room_id, owned_user_id}; | ||
use ruma_common::{owned_room_id, user_id, OwnedUserId}; | ||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; | ||
|
||
use super::{DirectEvent, DirectEventContent}; | ||
use crate::direct::{DirectUserIdentifier, OwnedDirectUserIdentifier}; | ||
|
||
#[test] | ||
fn serialization() { | ||
let mut content = DirectEventContent(BTreeMap::new()); | ||
let alice = owned_user_id!("@alice:ruma.io"); | ||
let alice = user_id!("@alice:ruma.io"); | ||
let alice_mail = "[email protected]"; | ||
let rooms = vec![owned_room_id!("!1:ruma.io")]; | ||
let mail_rooms = vec![owned_room_id!("!3:ruma.io")]; | ||
|
||
content.insert(alice.clone(), rooms.clone()); | ||
content.insert(alice.into(), rooms.clone()); | ||
content.insert(alice_mail.into(), mail_rooms.clone()); | ||
|
||
let json_data = json!({ | ||
alice: rooms, | ||
alice_mail: mail_rooms, | ||
}); | ||
|
||
assert_eq!(to_json_value(&content).unwrap(), json_data); | ||
} | ||
|
||
#[test] | ||
fn deserialization() { | ||
let alice = owned_user_id!("@alice:ruma.io"); | ||
let alice = user_id!("@alice:ruma.io"); | ||
let alice_mail = "[email protected]"; | ||
let rooms = vec![owned_room_id!("!1:ruma.io"), owned_room_id!("!2:ruma.io")]; | ||
let mail_rooms = vec![owned_room_id!("!3:ruma.io")]; | ||
|
||
let json_data = json!({ | ||
"content": { | ||
alice.to_string(): rooms, | ||
alice: rooms, | ||
alice_mail: mail_rooms, | ||
}, | ||
"type": "m.direct" | ||
}); | ||
|
||
let event: DirectEvent = from_json_value(json_data).unwrap(); | ||
let direct_rooms = event.content.get(&alice).unwrap(); | ||
|
||
let direct_rooms = event.content.get(<&DirectUserIdentifier>::from(alice)).unwrap(); | ||
assert!(direct_rooms.contains(&rooms[0])); | ||
assert!(direct_rooms.contains(&rooms[1])); | ||
|
||
let email_direct_rooms = | ||
event.content.get(<&DirectUserIdentifier>::from(alice_mail)).unwrap(); | ||
assert!(email_direct_rooms.contains(&mail_rooms[0])); | ||
} | ||
|
||
#[test] | ||
fn user_id_conversion() { | ||
let alice_direct_uid = DirectUserIdentifier::from_borrowed("@alice:ruma.io"); | ||
let alice_owned_user_id: OwnedUserId = alice_direct_uid | ||
.to_owned() | ||
.try_into() | ||
.expect("@alice:ruma.io should be convertible into a Matrix user ID"); | ||
assert_eq!(alice_direct_uid, alice_owned_user_id); | ||
|
||
let alice_direct_uid_mail = DirectUserIdentifier::from_borrowed("[email protected]"); | ||
OwnedUserId::try_from(alice_direct_uid_mail.to_owned()) | ||
.expect_err("[email protected] should not be convertible into a Matrix user ID"); | ||
|
||
let alice_user_id = user_id!("@alice:ruma.io"); | ||
let alice_direct_uid_mail: &DirectUserIdentifier = alice_user_id.into(); | ||
assert_eq!(alice_direct_uid_mail, alice_user_id); | ||
assert_eq!(alice_direct_uid_mail, alice_user_id.to_owned()); | ||
assert_eq!(alice_user_id, alice_direct_uid_mail); | ||
assert_eq!(alice_user_id.to_owned(), alice_direct_uid_mail); | ||
|
||
let alice_user_id = user_id!("@alice:ruma.io"); | ||
let alice_direct_uid_mail: OwnedDirectUserIdentifier = alice_user_id.into(); | ||
assert_eq!(alice_direct_uid_mail, alice_user_id); | ||
assert_eq!(alice_direct_uid_mail, alice_user_id.to_owned()); | ||
assert_eq!(alice_user_id, alice_direct_uid_mail); | ||
assert_eq!(alice_user_id.to_owned(), alice_direct_uid_mail); | ||
} | ||
} |