Add RoomVersionId
.
This commit is contained in:
parent
44de3bcb4d
commit
6d9b3c7bf2
@ -38,4 +38,5 @@ diesel_impl!(EventId);
|
||||
diesel_impl!(RoomAliasId);
|
||||
diesel_impl!(RoomId);
|
||||
diesel_impl!(RoomIdOrAliasId);
|
||||
diesel_impl!(RoomVersionId);
|
||||
diesel_impl!(UserId);
|
||||
|
@ -16,9 +16,9 @@ pub enum Error {
|
||||
InvalidCharacters,
|
||||
/// The domain part of the the ID string is not a valid IP address or DNS name.
|
||||
InvalidHost,
|
||||
/// The ID exceeds 255 bytes.
|
||||
/// The ID exceeds 255 bytes (or 32 codepoints for a room version ID.)
|
||||
MaximumLengthExceeded,
|
||||
/// The ID is less than 4 characters.
|
||||
/// The ID is less than 4 characters (or is an empty room version ID.)
|
||||
MinimumLengthNotSatisfied,
|
||||
/// The ID is missing the colon delimiter between localpart and server name.
|
||||
MissingDelimiter,
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! Crate **ruma_identifiers** contains types for [Matrix](https://matrix.org/) identifiers
|
||||
//! for events, rooms, room aliases, and users.
|
||||
//! for events, rooms, room aliases, room versions, and users.
|
||||
|
||||
#![deny(
|
||||
missing_copy_implementations,
|
||||
@ -40,7 +40,7 @@ pub use url::Host;
|
||||
|
||||
pub use crate::{
|
||||
error::Error, event_id::EventId, room_alias_id::RoomAliasId, room_id::RoomId,
|
||||
room_id_or_room_alias_id::RoomIdOrAliasId, user_id::UserId,
|
||||
room_id_or_room_alias_id::RoomIdOrAliasId, room_version_id::RoomVersionId, user_id::UserId,
|
||||
};
|
||||
|
||||
#[cfg(feature = "diesel")]
|
||||
@ -50,9 +50,10 @@ mod event_id;
|
||||
mod room_alias_id;
|
||||
mod room_id;
|
||||
mod room_id_or_room_alias_id;
|
||||
mod room_version_id;
|
||||
mod user_id;
|
||||
|
||||
/// All events must be 255 bytes or less.
|
||||
/// All identifiers must be 255 bytes or less.
|
||||
const MAX_BYTES: usize = 255;
|
||||
/// The minimum number of characters an ID can be.
|
||||
///
|
||||
|
351
src/room_version_id.rs
Normal file
351
src/room_version_id.rs
Normal file
@ -0,0 +1,351 @@
|
||||
//! Matrix room version identifiers.
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
fmt::{Display, Formatter, Result as FmtResult},
|
||||
};
|
||||
|
||||
#[cfg(feature = "diesel")]
|
||||
use diesel::sql_types::Text;
|
||||
use serde::{
|
||||
de::{Error as SerdeError, Unexpected, Visitor},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
/// Room version identifiers cannot be more than 32 code points.
|
||||
const MAX_CODE_POINTS: usize = 32;
|
||||
|
||||
/// A Matrix room version ID.
|
||||
///
|
||||
/// A `RoomVersionId` can be or converted or deserialized from a string slice, and can be converted
|
||||
/// or serialized back into a string as needed.
|
||||
///
|
||||
/// ```
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # use ruma_identifiers::RoomVersionId;
|
||||
/// assert_eq!(RoomVersionId::try_from("1").unwrap().to_string(), "1");
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "diesel", derive(FromSqlRow, QueryId, AsExpression, SqlType))]
|
||||
#[cfg_attr(feature = "diesel", sql_type = "Text")]
|
||||
pub struct RoomVersionId(InnerRoomVersionId);
|
||||
|
||||
/// Possibile values for room version, distinguishing between official Matrix versions and custom
|
||||
/// versions.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
enum InnerRoomVersionId {
|
||||
/// A version 1 room.
|
||||
Version1,
|
||||
/// A version 2 room.
|
||||
Version2,
|
||||
/// A version 3 room.
|
||||
Version3,
|
||||
/// A version 4 room.
|
||||
Version4,
|
||||
/// A custom room version.
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// A serde visitor for `RoomVersionId`.
|
||||
struct RoomVersionIdVisitor;
|
||||
|
||||
impl RoomVersionId {
|
||||
/// Creates a version 1 room ID.
|
||||
pub fn version_1() -> Self {
|
||||
Self(InnerRoomVersionId::Version1)
|
||||
}
|
||||
|
||||
/// Creates a version 2 room ID.
|
||||
pub fn version_2() -> Self {
|
||||
Self(InnerRoomVersionId::Version2)
|
||||
}
|
||||
|
||||
/// Creates a version 3 room ID.
|
||||
pub fn version_3() -> Self {
|
||||
Self(InnerRoomVersionId::Version3)
|
||||
}
|
||||
|
||||
/// Creates a version 4 room ID.
|
||||
pub fn version_4() -> Self {
|
||||
Self(InnerRoomVersionId::Version4)
|
||||
}
|
||||
|
||||
/// Creates a custom room version ID from the given string slice.
|
||||
pub fn custom(id: &str) -> Self {
|
||||
Self(InnerRoomVersionId::Custom(id.to_string()))
|
||||
}
|
||||
|
||||
/// Whether or not this room version is an official one specified by the Matrix protocol.
|
||||
pub fn is_official(&self) -> bool {
|
||||
!self.is_custom()
|
||||
}
|
||||
|
||||
/// Whether or not this is a custom room version.
|
||||
pub fn is_custom(&self) -> bool {
|
||||
match self.0 {
|
||||
InnerRoomVersionId::Custom(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether or not this is a version 1 room.
|
||||
pub fn is_version_1(&self) -> bool {
|
||||
self.0 == InnerRoomVersionId::Version1
|
||||
}
|
||||
|
||||
/// Whether or not this is a version 2 room.
|
||||
pub fn is_version_2(&self) -> bool {
|
||||
self.0 == InnerRoomVersionId::Version2
|
||||
}
|
||||
|
||||
/// Whether or not this is a version 3 room.
|
||||
pub fn is_version_3(&self) -> bool {
|
||||
self.0 == InnerRoomVersionId::Version3
|
||||
}
|
||||
|
||||
/// Whether or not this is a version 4 room.
|
||||
pub fn is_version_4(&self) -> bool {
|
||||
self.0 == InnerRoomVersionId::Version4
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RoomVersionId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
let message = match self.0 {
|
||||
InnerRoomVersionId::Version1 => "1",
|
||||
InnerRoomVersionId::Version2 => "2",
|
||||
InnerRoomVersionId::Version3 => "3",
|
||||
InnerRoomVersionId::Version4 => "4",
|
||||
InnerRoomVersionId::Custom(ref version) => version,
|
||||
};
|
||||
|
||||
write!(f, "{}", message)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for RoomVersionId {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for RoomVersionId {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(RoomVersionIdVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for RoomVersionId {
|
||||
type Error = Error;
|
||||
|
||||
/// Attempts to create a new Matrix room version ID from a string representation.
|
||||
fn try_from(room_version_id: &'a str) -> Result<Self, Error> {
|
||||
let version = match room_version_id {
|
||||
"1" => Self(InnerRoomVersionId::Version1),
|
||||
"2" => Self(InnerRoomVersionId::Version2),
|
||||
"3" => Self(InnerRoomVersionId::Version3),
|
||||
"4" => Self(InnerRoomVersionId::Version4),
|
||||
custom => {
|
||||
if custom.is_empty() {
|
||||
return Err(Error::MinimumLengthNotSatisfied);
|
||||
} else if custom.chars().count() > MAX_CODE_POINTS {
|
||||
return Err(Error::MaximumLengthExceeded);
|
||||
} else {
|
||||
Self(InnerRoomVersionId::Custom(custom.to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(version)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Visitor<'de> for RoomVersionIdVisitor {
|
||||
type Value = RoomVersionId;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(formatter, "a Matrix room version ID as a string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
match RoomVersionId::try_from(v) {
|
||||
Ok(room_id) => Ok(room_id),
|
||||
Err(_) => Err(SerdeError::invalid_value(Unexpected::Str(v), &self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use serde_json::{from_str, to_string};
|
||||
|
||||
use super::RoomVersionId;
|
||||
use crate::error::Error;
|
||||
|
||||
#[test]
|
||||
fn valid_version_1_room_version_id() {
|
||||
assert_eq!(
|
||||
RoomVersionId::try_from("1")
|
||||
.expect("Failed to create RoomVersionId.")
|
||||
.to_string(),
|
||||
"1"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn valid_version_2_room_version_id() {
|
||||
assert_eq!(
|
||||
RoomVersionId::try_from("2")
|
||||
.expect("Failed to create RoomVersionId.")
|
||||
.to_string(),
|
||||
"2"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn valid_version_3_room_version_id() {
|
||||
assert_eq!(
|
||||
RoomVersionId::try_from("3")
|
||||
.expect("Failed to create RoomVersionId.")
|
||||
.to_string(),
|
||||
"3"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn valid_version_4_room_version_id() {
|
||||
assert_eq!(
|
||||
RoomVersionId::try_from("4")
|
||||
.expect("Failed to create RoomVersionId.")
|
||||
.to_string(),
|
||||
"4"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_custom_room_version_id() {
|
||||
assert_eq!(
|
||||
RoomVersionId::try_from("io.ruma.1")
|
||||
.expect("Failed to create RoomVersionId.")
|
||||
.to_string(),
|
||||
"io.ruma.1"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_room_version_id() {
|
||||
assert_eq!(
|
||||
RoomVersionId::try_from(""),
|
||||
Err(Error::MinimumLengthNotSatisfied)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn over_max_code_point_room_version_id() {
|
||||
assert_eq!(
|
||||
RoomVersionId::try_from("0123456789012345678901234567890123456789"),
|
||||
Err(Error::MaximumLengthExceeded)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_official_room_id() {
|
||||
assert_eq!(
|
||||
to_string(&RoomVersionId::try_from("1").expect("Failed to create RoomVersionId."))
|
||||
.expect("Failed to convert RoomVersionId to JSON."),
|
||||
r#""1""#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_official_room_id() {
|
||||
let deserialized =
|
||||
from_str::<RoomVersionId>(r#""1""#).expect("Failed to convert RoomVersionId to JSON.");
|
||||
|
||||
assert!(deserialized.is_version_1());
|
||||
assert!(deserialized.is_official());
|
||||
|
||||
assert_eq!(
|
||||
deserialized,
|
||||
RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_custom_room_id() {
|
||||
assert_eq!(
|
||||
to_string(
|
||||
&RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
|
||||
)
|
||||
.expect("Failed to convert RoomVersionId to JSON."),
|
||||
r#""io.ruma.1""#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_custom_room_id() {
|
||||
let deserialized = from_str::<RoomVersionId>(r#""io.ruma.1""#)
|
||||
.expect("Failed to convert RoomVersionId to JSON.");
|
||||
|
||||
assert!(deserialized.is_custom());
|
||||
|
||||
assert_eq!(
|
||||
deserialized,
|
||||
RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constructors() {
|
||||
assert!(RoomVersionId::version_1().is_version_1());
|
||||
assert!(RoomVersionId::version_2().is_version_2());
|
||||
assert!(RoomVersionId::version_3().is_version_3());
|
||||
assert!(RoomVersionId::version_4().is_version_4());
|
||||
assert!(RoomVersionId::custom("foo").is_custom());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn predicate_methods() {
|
||||
let version_1 = RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.");
|
||||
let version_2 = RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.");
|
||||
let version_3 = RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.");
|
||||
let version_4 = RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.");
|
||||
let custom = RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.");
|
||||
|
||||
assert!(version_1.is_version_1());
|
||||
assert!(version_2.is_version_2());
|
||||
assert!(version_3.is_version_3());
|
||||
assert!(version_4.is_version_4());
|
||||
|
||||
assert!(!version_1.is_version_2());
|
||||
assert!(!version_1.is_version_3());
|
||||
assert!(!version_1.is_version_4());
|
||||
|
||||
assert!(version_1.is_official());
|
||||
assert!(version_2.is_official());
|
||||
assert!(version_3.is_official());
|
||||
assert!(version_4.is_official());
|
||||
|
||||
assert!(!version_1.is_custom());
|
||||
assert!(!version_2.is_custom());
|
||||
assert!(!version_3.is_custom());
|
||||
assert!(!version_4.is_custom());
|
||||
|
||||
assert!(custom.is_custom());
|
||||
assert!(!custom.is_official());
|
||||
assert!(!custom.is_version_1());
|
||||
assert!(!custom.is_version_2());
|
||||
assert!(!custom.is_version_3());
|
||||
assert!(!custom.is_version_4());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user