Identifiers now always own just a single allocation, and implement AsRef<str>. PartialOrd and Ord implementations were also added at the same time. All identifiers except RoomVersionId can now also be converted to String without allocation.
173 lines
5.3 KiB
Rust
173 lines
5.3 KiB
Rust
//! Matrix identifiers for places where a room ID or room alias ID are used interchangeably.
|
|
|
|
use std::{borrow::Cow, convert::TryFrom, hint::unreachable_unchecked, num::NonZeroU8};
|
|
|
|
#[cfg(feature = "diesel")]
|
|
use diesel::sql_types::Text;
|
|
|
|
use crate::{error::Error, parse_id};
|
|
|
|
/// A Matrix room ID or a Matrix room alias ID.
|
|
///
|
|
/// `RoomIdOrAliasId` is useful for APIs that accept either kind of room identifier. It is converted
|
|
/// from a string slice, and can be converted back into a string as needed. When converted from a
|
|
/// string slice, the variant is determined by the leading sigil character.
|
|
///
|
|
/// ```
|
|
/// # use std::convert::TryFrom;
|
|
/// # use ruma_identifiers::RoomIdOrAliasId;
|
|
/// assert_eq!(
|
|
/// RoomIdOrAliasId::try_from("#ruma:example.com").unwrap().as_ref(),
|
|
/// "#ruma:example.com"
|
|
/// );
|
|
///
|
|
/// assert_eq!(
|
|
/// RoomIdOrAliasId::try_from("!n8f893n9:example.com").unwrap().as_ref(),
|
|
/// "!n8f893n9:example.com"
|
|
/// );
|
|
/// ```
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(feature = "diesel", derive(FromSqlRow, QueryId, AsExpression, SqlType))]
|
|
#[cfg_attr(feature = "diesel", sql_type = "Text")]
|
|
pub struct RoomIdOrAliasId {
|
|
full_id: String,
|
|
colon_idx: NonZeroU8,
|
|
}
|
|
|
|
impl RoomIdOrAliasId {
|
|
/// Returns the host of the room (alias) ID, containing the server name (including the port) of
|
|
/// the originating homeserver.
|
|
pub fn hostname(&self) -> &str {
|
|
&self.full_id[self.colon_idx.get() as usize + 1..]
|
|
}
|
|
|
|
/// Returns the local part (everything after the `!` or `#` and before the first colon).
|
|
pub fn localpart(&self) -> &str {
|
|
&self.full_id[1..self.colon_idx.get() as usize]
|
|
}
|
|
|
|
/// Whether this is a room id (starts with `'!'`)
|
|
pub fn is_room_id(&self) -> bool {
|
|
self.variant() == Variant::RoomId
|
|
}
|
|
|
|
/// Whether this is a room alias id (starts with `'#'`)
|
|
pub fn is_room_alias_id(&self) -> bool {
|
|
self.variant() == Variant::RoomAliasId
|
|
}
|
|
|
|
fn variant(&self) -> Variant {
|
|
match self.full_id.bytes().next() {
|
|
Some(b'!') => Variant::RoomId,
|
|
Some(b'#') => Variant::RoomAliasId,
|
|
_ => unsafe { unreachable_unchecked() },
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq)]
|
|
enum Variant {
|
|
RoomId,
|
|
RoomAliasId,
|
|
}
|
|
|
|
impl TryFrom<Cow<'_, str>> for RoomIdOrAliasId {
|
|
type Error = Error;
|
|
|
|
/// Attempts to create a new Matrix room ID or a room alias ID from a string representation.
|
|
///
|
|
/// The string must either include the leading ! sigil, the localpart, a literal colon, and a
|
|
/// valid homeserver host or include the leading # sigil, the alias, a literal colon, and a
|
|
/// valid homeserver host.
|
|
fn try_from(room_id_or_alias_id: Cow<'_, str>) -> Result<Self, Error> {
|
|
let colon_idx = parse_id(&room_id_or_alias_id, &['#', '!'])?;
|
|
Ok(Self {
|
|
full_id: room_id_or_alias_id.into_owned(),
|
|
colon_idx,
|
|
})
|
|
}
|
|
}
|
|
|
|
common_impls!(RoomIdOrAliasId, "a Matrix room ID or room alias ID");
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::convert::TryFrom;
|
|
|
|
use serde_json::{from_str, to_string};
|
|
|
|
use super::RoomIdOrAliasId;
|
|
use crate::error::Error;
|
|
|
|
#[test]
|
|
fn valid_room_id_or_alias_id_with_a_room_alias_id() {
|
|
assert_eq!(
|
|
RoomIdOrAliasId::try_from("#ruma:example.com")
|
|
.expect("Failed to create RoomAliasId.")
|
|
.as_ref(),
|
|
"#ruma:example.com"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn valid_room_id_or_alias_id_with_a_room_id() {
|
|
assert_eq!(
|
|
RoomIdOrAliasId::try_from("!29fhd83h92h0:example.com")
|
|
.expect("Failed to create RoomId.")
|
|
.as_ref(),
|
|
"!29fhd83h92h0:example.com"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn missing_sigil_for_room_id_or_alias_id() {
|
|
assert_eq!(
|
|
RoomIdOrAliasId::try_from("ruma:example.com").unwrap_err(),
|
|
Error::MissingSigil
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn serialize_valid_room_id_or_alias_id_with_a_room_alias_id() {
|
|
assert_eq!(
|
|
to_string(
|
|
&RoomIdOrAliasId::try_from("#ruma:example.com")
|
|
.expect("Failed to create RoomAliasId.")
|
|
)
|
|
.expect("Failed to convert RoomAliasId to JSON."),
|
|
r##""#ruma:example.com""##
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn serialize_valid_room_id_or_alias_id_with_a_room_id() {
|
|
assert_eq!(
|
|
to_string(
|
|
&RoomIdOrAliasId::try_from("!29fhd83h92h0:example.com")
|
|
.expect("Failed to create RoomId.")
|
|
)
|
|
.expect("Failed to convert RoomId to JSON."),
|
|
r#""!29fhd83h92h0:example.com""#
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn deserialize_valid_room_id_or_alias_id_with_a_room_alias_id() {
|
|
assert_eq!(
|
|
from_str::<RoomIdOrAliasId>(r##""#ruma:example.com""##)
|
|
.expect("Failed to convert JSON to RoomAliasId"),
|
|
RoomIdOrAliasId::try_from("#ruma:example.com").expect("Failed to create RoomAliasId.")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn deserialize_valid_room_id_or_alias_id_with_a_room_id() {
|
|
assert_eq!(
|
|
from_str::<RoomIdOrAliasId>(r##""!29fhd83h92h0:example.com""##)
|
|
.expect("Failed to convert JSON to RoomId"),
|
|
RoomIdOrAliasId::try_from("!29fhd83h92h0:example.com")
|
|
.expect("Failed to create RoomAliasId.")
|
|
);
|
|
}
|
|
}
|