Add borrowed id types

This commit is contained in:
Jonas Platte 2020-05-30 21:01:03 +02:00
parent 06c52c2abb
commit 622d69884c
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
9 changed files with 226 additions and 144 deletions

View File

@ -6,8 +6,8 @@ sources:
tasks:
- rustup: |
# We specify --profile minimal because we'd otherwise download docs
rustup toolchain install 1.36.0 --profile minimal
rustup default 1.36.0
rustup toolchain install 1.42.0 --profile minimal
rustup default 1.42.0
- test: |
cd ruma-identifiers

View File

@ -37,13 +37,13 @@ use crate::{error::Error, parse_id, validate_id};
/// "$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg"
/// );
/// ```
#[derive(Clone, Debug)]
pub struct EventId {
full_id: Box<str>,
#[derive(Clone, Copy, Debug)]
pub struct EventId<T> {
full_id: T,
colon_idx: Option<NonZeroU8>,
}
impl EventId {
impl<T> EventId<T> {
/// Attempts to generate an `EventId` for the given origin server with a localpart consisting
/// of 18 random ASCII characters. This should only be used for events in the original format
/// as used by Matrix room versions 1 and 2.
@ -52,7 +52,10 @@ impl EventId {
/// parsed as a valid host.
#[cfg(feature = "rand")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
pub fn new(server_name: &str) -> Result<Self, Error> {
pub fn new(server_name: &str) -> Result<Self, Error>
where
String: Into<T>,
{
use crate::{generate_localpart, is_valid_server_name};
if !is_valid_server_name(server_name) {
@ -69,21 +72,27 @@ impl EventId {
/// Returns the event's unique ID. For the original event format as used by Matrix room
/// versions 1 and 2, this is the "localpart" that precedes the homeserver. For later formats,
/// this is the entire ID without the leading $ sigil.
pub fn localpart(&self) -> &str {
pub fn localpart(&self) -> &str
where
T: AsRef<str>,
{
let idx = match self.colon_idx {
Some(idx) => idx.get() as usize,
None => self.full_id.len(),
None => self.full_id.as_ref().len(),
};
&self.full_id[1..idx]
&self.full_id.as_ref()[1..idx]
}
/// Returns the server name of the event ID.
///
/// Only applicable to events in the original format as used by Matrix room versions 1 and 2.
pub fn server_name(&self) -> Option<&str> {
pub fn server_name(&self) -> Option<&str>
where
T: AsRef<str>,
{
self.colon_idx
.map(|idx| &self.full_id[idx.get() as usize + 1..])
.map(|idx| &self.full_id.as_ref()[idx.get() as usize + 1..])
}
}
@ -91,9 +100,9 @@ impl EventId {
///
/// If using the original event format as used by Matrix room versions 1 and 2, the string must
/// include the leading $ sigil, the localpart, a literal colon, and a valid homeserver hostname.
fn try_from<S>(event_id: S) -> Result<EventId, Error>
fn try_from<S, T>(event_id: S) -> Result<EventId<T>, Error>
where
S: AsRef<str> + Into<Box<str>>,
S: AsRef<str> + Into<T>,
{
if event_id.as_ref().contains(':') {
let colon_idx = parse_id(event_id.as_ref(), &['$'])?;
@ -121,9 +130,10 @@ mod tests {
#[cfg(feature = "serde")]
use serde_json::{from_str, to_string};
use super::EventId;
use crate::error::Error;
type EventId = super::EventId<Box<str>>;
#[test]
fn valid_original_event_id() {
assert_eq!(

View File

@ -5,7 +5,7 @@
#![deny(
missing_copy_implementations,
missing_debug_implementations,
missing_docs
//missing_docs
)]
// Since we support Rust 1.36.0, we can't apply this suggestion yet
#![allow(clippy::use_self)]
@ -17,25 +17,52 @@ use std::num::NonZeroU8;
use serde::de::{self, Deserialize as _, Deserializer, Unexpected};
#[doc(inline)]
pub use crate::{
device_id::DeviceId, error::Error, event_id::EventId, room_alias_id::RoomAliasId,
room_id::RoomId, room_id_or_room_alias_id::RoomIdOrAliasId, room_version_id::RoomVersionId,
server_name::is_valid_server_name, user_id::UserId,
};
pub use crate::{error::Error, server_name::is_valid_server_name};
#[macro_use]
mod macros;
pub mod device_id;
mod error;
mod event_id;
mod room_alias_id;
mod room_id;
mod room_id_or_room_alias_id;
mod room_version_id;
mod server_name;
pub mod device_id;
pub mod event_id;
pub mod room_alias_id;
pub mod room_id;
pub mod room_id_or_room_alias_id;
pub mod room_version_id;
pub mod user_id;
/// An owned event ID.
pub type EventId = event_id::EventId<Box<str>>;
/// A reference to an event ID.
pub type EventIdRef<'a> = event_id::EventId<&'a str>;
/// An owned room alias ID.
pub type RoomAliasId = room_alias_id::RoomAliasId<Box<str>>;
/// A reference to a room alias ID.
pub type RoomAliasIdRef<'a> = room_alias_id::RoomAliasId<&'a str>;
/// An owned room ID.
pub type RoomId = room_id::RoomId<Box<str>>;
/// A reference to a room ID.
pub type RoomIdRef<'a> = room_id::RoomId<&'a str>;
/// An owned room alias ID or room ID.
pub type RoomIdOrAliasId = room_id_or_room_alias_id::RoomIdOrAliasId<Box<str>>;
/// A reference to a room alias ID or room ID.
pub type RoomIdOrAliasIdRef<'a> = room_id_or_room_alias_id::RoomIdOrAliasId<&'a str>;
/// An owned room version ID.
pub type RoomVersionId = room_version_id::RoomVersionId<Box<str>>;
/// A reference to a room version ID.
pub type RoomVersionIdRef<'a> = room_version_id::RoomVersionId<&'a str>;
/// An owned user ID.
pub type UserId = user_id::UserId<Box<str>>;
/// A reference to a user ID.
pub type UserIdRef<'a> = user_id::UserId<&'a str>;
/// All identifiers must be 255 bytes or less.
const MAX_BYTES: usize = 255;
/// The minimum number of characters an ID can be.

View File

@ -1,12 +1,20 @@
macro_rules! common_impls {
($id:ident, $try_from:ident, $desc:literal) => {
impl ::std::convert::From<$id> for ::std::string::String {
fn from(id: $id) -> Self {
impl ::std::convert::From<$id<Box<str>>> for ::std::string::String {
fn from(id: $id<Box<str>>) -> Self {
id.full_id.into()
}
}
impl ::std::convert::TryFrom<&str> for $id {
impl<'a> ::std::convert::TryFrom<&'a str> for $id<&'a str> {
type Error = crate::error::Error;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
$try_from(s)
}
}
impl ::std::convert::TryFrom<&str> for $id<Box<str>> {
type Error = crate::error::Error;
fn try_from(s: &str) -> Result<Self, Self::Error> {
@ -14,7 +22,7 @@ macro_rules! common_impls {
}
}
impl ::std::convert::TryFrom<String> for $id {
impl ::std::convert::TryFrom<String> for $id<Box<str>> {
type Error = crate::error::Error;
fn try_from(s: String) -> Result<Self, Self::Error> {
@ -22,56 +30,56 @@ macro_rules! common_impls {
}
}
impl ::std::convert::AsRef<str> for $id {
impl<T: ::std::convert::AsRef<str>> ::std::convert::AsRef<str> for $id<T> {
fn as_ref(&self) -> &str {
&self.full_id
self.full_id.as_ref()
}
}
impl ::std::fmt::Display for $id {
impl<T: ::std::fmt::Display> ::std::fmt::Display for $id<T> {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{}", self.full_id)
}
}
impl ::std::cmp::PartialEq for $id {
impl<T: ::std::cmp::PartialEq> ::std::cmp::PartialEq for $id<T> {
fn eq(&self, other: &Self) -> bool {
self.full_id == other.full_id
}
}
impl ::std::cmp::Eq for $id {}
impl<T: ::std::cmp::Eq> ::std::cmp::Eq for $id<T> {}
impl ::std::cmp::PartialOrd for $id {
impl<T: ::std::cmp::PartialOrd> ::std::cmp::PartialOrd for $id<T> {
fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
::std::cmp::PartialOrd::partial_cmp(&self.full_id, &other.full_id)
}
}
impl ::std::cmp::Ord for $id {
impl<T: ::std::cmp::Ord> ::std::cmp::Ord for $id<T> {
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
::std::cmp::Ord::cmp(&self.full_id, &other.full_id)
}
}
impl ::std::hash::Hash for $id {
impl<T: ::std::hash::Hash> ::std::hash::Hash for $id<T> {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
self.full_id.hash(state);
}
}
#[cfg(feature = "serde")]
impl ::serde::Serialize for $id {
impl<T: AsRef<str>> ::serde::Serialize for $id<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
serializer.serialize_str(&self.full_id)
serializer.serialize_str(self.full_id.as_ref())
}
}
#[cfg(feature = "serde")]
impl<'de> ::serde::Deserialize<'de> for $id {
impl<'de> ::serde::Deserialize<'de> for $id<Box<str>> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
@ -80,27 +88,27 @@ macro_rules! common_impls {
}
}
impl ::std::cmp::PartialEq<&str> for $id {
impl<T: AsRef<str>> ::std::cmp::PartialEq<&str> for $id<T> {
fn eq(&self, other: &&str) -> bool {
&self.full_id[..] == *other
self.full_id.as_ref() == *other
}
}
impl ::std::cmp::PartialEq<$id> for &str {
fn eq(&self, other: &$id) -> bool {
*self == &other.full_id[..]
impl<T: AsRef<str>> ::std::cmp::PartialEq<$id<T>> for &str {
fn eq(&self, other: &$id<T>) -> bool {
*self == other.full_id.as_ref()
}
}
impl ::std::cmp::PartialEq<::std::string::String> for $id {
impl<T: AsRef<str>> ::std::cmp::PartialEq<::std::string::String> for $id<T> {
fn eq(&self, other: &::std::string::String) -> bool {
&self.full_id[..] == &other[..]
self.full_id.as_ref() == &other[..]
}
}
impl ::std::cmp::PartialEq<$id> for ::std::string::String {
fn eq(&self, other: &$id) -> bool {
&self[..] == &other.full_id[..]
impl<T: AsRef<str>> ::std::cmp::PartialEq<$id<T>> for ::std::string::String {
fn eq(&self, other: &$id<T>) -> bool {
&self[..] == other.full_id.as_ref()
}
}
};

View File

@ -17,30 +17,30 @@ use crate::{error::Error, parse_id};
/// "#ruma:example.com"
/// );
/// ```
#[derive(Clone, Debug)]
pub struct RoomAliasId {
pub(crate) full_id: Box<str>,
#[derive(Clone, Copy, Debug)]
pub struct RoomAliasId<T> {
pub(crate) full_id: T,
pub(crate) colon_idx: NonZeroU8,
}
impl RoomAliasId {
impl<T: AsRef<str>> RoomAliasId<T> {
/// Returns the room's alias.
pub fn alias(&self) -> &str {
&self.full_id[1..self.colon_idx.get() as usize]
&self.full_id.as_ref()[1..self.colon_idx.get() as usize]
}
/// Returns the server name of the room alias ID.
pub fn server_name(&self) -> &str {
&self.full_id[self.colon_idx.get() as usize + 1..]
&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]
}
}
/// Attempts to create a new Matrix room alias ID from a string representation.
///
/// The string must include the leading # sigil, the alias, a literal colon, and a server name.
fn try_from<S>(room_id: S) -> Result<RoomAliasId, Error>
fn try_from<S, T>(room_id: S) -> Result<RoomAliasId<T>, Error>
where
S: AsRef<str> + Into<Box<str>>,
S: AsRef<str> + Into<T>,
{
let colon_idx = parse_id(room_id.as_ref(), &['#'])?;
@ -59,9 +59,10 @@ mod tests {
#[cfg(feature = "serde")]
use serde_json::{from_str, to_string};
use super::RoomAliasId;
use crate::error::Error;
type RoomAliasId = super::RoomAliasId<Box<str>>;
#[test]
fn valid_room_alias_id() {
assert_eq!(

View File

@ -17,20 +17,23 @@ use crate::{error::Error, parse_id};
/// "!n8f893n9:example.com"
/// );
/// ```
#[derive(Clone, Debug)]
pub struct RoomId {
pub(crate) full_id: Box<str>,
#[derive(Clone, Copy, Debug)]
pub struct RoomId<T> {
pub(crate) full_id: T,
pub(crate) colon_idx: NonZeroU8,
}
impl RoomId {
impl<T> RoomId<T> {
/// Attempts to generate a `RoomId` for the given origin server with a localpart consisting of
/// 18 random ASCII characters.
///
/// Fails if the given homeserver cannot be parsed as a valid host.
#[cfg(feature = "rand")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
pub fn new(server_name: &str) -> Result<Self, Error> {
pub fn new(server_name: &str) -> Result<Self, Error>
where
String: Into<T>,
{
use crate::{generate_localpart, is_valid_server_name};
if !is_valid_server_name(server_name) {
@ -45,22 +48,28 @@ impl RoomId {
}
/// Returns the rooms's unique ID.
pub fn localpart(&self) -> &str {
&self.full_id[1..self.colon_idx.get() as usize]
pub fn localpart(&self) -> &str
where
T: AsRef<str>,
{
&self.full_id.as_ref()[1..self.colon_idx.get() as usize]
}
/// Returns the server name of the room ID.
pub fn server_name(&self) -> &str {
&self.full_id[self.colon_idx.get() as usize + 1..]
pub fn server_name(&self) -> &str
where
T: AsRef<str>,
{
&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]
}
}
/// Attempts to create a new Matrix room ID from a string representation.
///
/// The string must include the leading ! sigil, the localpart, a literal colon, and a server name.
fn try_from<S>(room_id: S) -> Result<RoomId, Error>
fn try_from<S, T>(room_id: S) -> Result<RoomId<T>, Error>
where
S: AsRef<str> + Into<Box<str>>,
S: AsRef<str> + Into<T>,
{
let colon_idx = parse_id(room_id.as_ref(), &['!'])?;
@ -79,9 +88,10 @@ mod tests {
#[cfg(feature = "serde")]
use serde_json::{from_str, to_string};
use super::RoomId;
use crate::error::Error;
type RoomId = super::RoomId<Box<str>>;
#[test]
fn valid_room_id() {
assert_eq!(

View File

@ -2,7 +2,7 @@
use std::{convert::TryFrom, hint::unreachable_unchecked, num::NonZeroU8};
use crate::{error::Error, parse_id, RoomAliasId, RoomId};
use crate::{error::Error, parse_id, room_alias_id::RoomAliasId, room_id::RoomId};
/// A Matrix room ID or a Matrix room alias ID.
///
@ -23,21 +23,21 @@ use crate::{error::Error, parse_id, RoomAliasId, RoomId};
/// "!n8f893n9:example.com"
/// );
/// ```
#[derive(Clone, Debug)]
pub struct RoomIdOrAliasId {
full_id: Box<str>,
#[derive(Clone, Copy, Debug)]
pub struct RoomIdOrAliasId<T> {
full_id: T,
colon_idx: NonZeroU8,
}
impl RoomIdOrAliasId {
impl<T: AsRef<str>> RoomIdOrAliasId<T> {
/// 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]
&self.full_id.as_ref()[1..self.colon_idx.get() as usize]
}
/// Returns the server name of the room (alias) ID.
pub fn server_name(&self) -> &str {
&self.full_id[self.colon_idx.get() as usize + 1..]
&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]
}
/// Whether this is a room id (starts with `'!'`)
@ -53,7 +53,7 @@ impl RoomIdOrAliasId {
/// Turn this `RoomIdOrAliasId` into `Either<RoomId, RoomAliasId>`
#[cfg(feature = "either")]
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
pub fn into_either(self) -> either::Either<RoomId, RoomAliasId> {
pub fn into_either(self) -> either::Either<RoomId<T>, RoomAliasId<T>> {
match self.variant() {
Variant::RoomId => either::Either::Left(RoomId {
full_id: self.full_id,
@ -67,7 +67,7 @@ impl RoomIdOrAliasId {
}
fn variant(&self) -> Variant {
match self.full_id.bytes().next() {
match self.full_id.as_ref().bytes().next() {
Some(b'!') => Variant::RoomId,
Some(b'#') => Variant::RoomAliasId,
_ => unsafe { unreachable_unchecked() },
@ -86,9 +86,9 @@ enum Variant {
/// 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<S>(room_id_or_alias_id: S) -> Result<RoomIdOrAliasId, Error>
fn try_from<S, T>(room_id_or_alias_id: S) -> Result<RoomIdOrAliasId<T>, Error>
where
S: AsRef<str> + Into<Box<str>>,
S: AsRef<str> + Into<T>,
{
let colon_idx = parse_id(room_id_or_alias_id.as_ref(), &['#', '!'])?;
Ok(RoomIdOrAliasId {
@ -103,22 +103,22 @@ common_impls!(
"a Matrix room ID or room alias ID"
);
impl From<RoomId> for RoomIdOrAliasId {
fn from(RoomId { full_id, colon_idx }: RoomId) -> Self {
impl<T> From<RoomId<T>> for RoomIdOrAliasId<T> {
fn from(RoomId { full_id, colon_idx }: RoomId<T>) -> Self {
Self { full_id, colon_idx }
}
}
impl From<RoomAliasId> for RoomIdOrAliasId {
fn from(RoomAliasId { full_id, colon_idx }: RoomAliasId) -> Self {
impl<T> From<RoomAliasId<T>> for RoomIdOrAliasId<T> {
fn from(RoomAliasId { full_id, colon_idx }: RoomAliasId<T>) -> Self {
Self { full_id, colon_idx }
}
}
impl TryFrom<RoomIdOrAliasId> for RoomId {
type Error = RoomAliasId;
impl<T: AsRef<str>> TryFrom<RoomIdOrAliasId<T>> for RoomId<T> {
type Error = RoomAliasId<T>;
fn try_from(id: RoomIdOrAliasId) -> Result<RoomId, RoomAliasId> {
fn try_from(id: RoomIdOrAliasId<T>) -> Result<RoomId<T>, RoomAliasId<T>> {
match id.variant() {
Variant::RoomId => Ok(RoomId {
full_id: id.full_id,
@ -132,10 +132,10 @@ impl TryFrom<RoomIdOrAliasId> for RoomId {
}
}
impl TryFrom<RoomIdOrAliasId> for RoomAliasId {
type Error = RoomId;
impl<T: AsRef<str>> TryFrom<RoomIdOrAliasId<T>> for RoomAliasId<T> {
type Error = RoomId<T>;
fn try_from(id: RoomIdOrAliasId) -> Result<RoomAliasId, RoomId> {
fn try_from(id: RoomIdOrAliasId<T>) -> Result<RoomAliasId<T>, RoomId<T>> {
match id.variant() {
Variant::RoomAliasId => Ok(RoomAliasId {
full_id: id.full_id,
@ -156,9 +156,10 @@ mod tests {
#[cfg(feature = "serde")]
use serde_json::{from_str, to_string};
use super::RoomIdOrAliasId;
use crate::error::Error;
type RoomIdOrAliasId = super::RoomIdOrAliasId<Box<str>>;
#[test]
fn valid_room_id_or_alias_id_with_a_room_alias_id() {
assert_eq!(

View File

@ -24,13 +24,13 @@ const MAX_CODE_POINTS: usize = 32;
/// # use ruma_identifiers::RoomVersionId;
/// assert_eq!(RoomVersionId::try_from("1").unwrap().as_ref(), "1");
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RoomVersionId(InnerRoomVersionId);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RoomVersionId<T>(InnerRoomVersionId<T>);
/// Possibile values for room version, distinguishing between official Matrix versions and custom
/// versions.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum InnerRoomVersionId {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum InnerRoomVersionId<T> {
/// A version 1 room.
Version1,
@ -50,10 +50,10 @@ enum InnerRoomVersionId {
Version6,
/// A custom room version.
Custom(Box<str>),
Custom(T),
}
impl RoomVersionId {
impl<T> RoomVersionId<T> {
/// Creates a version 1 room ID.
pub fn version_1() -> Self {
Self(InnerRoomVersionId::Version1)
@ -85,7 +85,10 @@ impl RoomVersionId {
}
/// Creates a custom room version ID from the given string slice.
pub fn custom(id: String) -> Self {
pub fn custom(id: String) -> Self
where
String: Into<T>,
{
Self(InnerRoomVersionId::Custom(id.into()))
}
@ -104,37 +107,37 @@ impl RoomVersionId {
/// Whether or not this is a version 1 room.
pub fn is_version_1(&self) -> bool {
self.0 == InnerRoomVersionId::Version1
matches!(self.0, InnerRoomVersionId::Version1)
}
/// Whether or not this is a version 2 room.
pub fn is_version_2(&self) -> bool {
self.0 == InnerRoomVersionId::Version2
matches!(self.0, InnerRoomVersionId::Version2)
}
/// Whether or not this is a version 3 room.
pub fn is_version_3(&self) -> bool {
self.0 == InnerRoomVersionId::Version3
matches!(self.0, InnerRoomVersionId::Version3)
}
/// Whether or not this is a version 4 room.
pub fn is_version_4(&self) -> bool {
self.0 == InnerRoomVersionId::Version4
matches!(self.0, InnerRoomVersionId::Version4)
}
/// Whether or not this is a version 5 room.
pub fn is_version_5(&self) -> bool {
self.0 == InnerRoomVersionId::Version5
matches!(self.0, InnerRoomVersionId::Version5)
}
/// Whether or not this is a version 6 room.
pub fn is_version_6(&self) -> bool {
self.0 == InnerRoomVersionId::Version5
matches!(self.0, InnerRoomVersionId::Version5)
}
}
impl From<RoomVersionId> for String {
fn from(id: RoomVersionId) -> Self {
impl From<RoomVersionId<Box<str>>> for String {
fn from(id: RoomVersionId<Box<str>>) -> Self {
match id.0 {
InnerRoomVersionId::Version1 => "1".to_owned(),
InnerRoomVersionId::Version2 => "2".to_owned(),
@ -147,7 +150,7 @@ impl From<RoomVersionId> for String {
}
}
impl AsRef<str> for RoomVersionId {
impl<T: AsRef<str>> AsRef<str> for RoomVersionId<T> {
fn as_ref(&self) -> &str {
match &self.0 {
InnerRoomVersionId::Version1 => "1",
@ -156,31 +159,31 @@ impl AsRef<str> for RoomVersionId {
InnerRoomVersionId::Version4 => "4",
InnerRoomVersionId::Version5 => "5",
InnerRoomVersionId::Version6 => "6",
InnerRoomVersionId::Custom(version) => version,
InnerRoomVersionId::Custom(version) => version.as_ref(),
}
}
}
impl Display for RoomVersionId {
impl<T: AsRef<str>> Display for RoomVersionId<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_ref())
}
}
impl PartialOrd for RoomVersionId {
fn partial_cmp(&self, other: &RoomVersionId) -> Option<Ordering> {
impl<T: PartialEq + AsRef<str>> PartialOrd for RoomVersionId<T> {
fn partial_cmp(&self, other: &RoomVersionId<T>) -> Option<Ordering> {
self.as_ref().partial_cmp(other.as_ref())
}
}
impl Ord for RoomVersionId {
impl<T: Eq + AsRef<str>> Ord for RoomVersionId<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.as_ref().cmp(other.as_ref())
}
}
#[cfg(feature = "serde")]
impl Serialize for RoomVersionId {
impl<T: AsRef<str>> Serialize for RoomVersionId<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
@ -190,7 +193,7 @@ impl Serialize for RoomVersionId {
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for RoomVersionId {
impl<'de> Deserialize<'de> for RoomVersionId<Box<str>> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
@ -200,9 +203,9 @@ impl<'de> Deserialize<'de> for RoomVersionId {
}
/// Attempts to create a new Matrix room version ID from a string representation.
fn try_from<S>(room_version_id: S) -> Result<RoomVersionId, Error>
fn try_from<S, T>(room_version_id: S) -> Result<RoomVersionId<T>, Error>
where
S: AsRef<str> + Into<Box<str>>,
S: AsRef<str> + Into<T>,
{
let version = match room_version_id.as_ref() {
"1" => RoomVersionId(InnerRoomVersionId::Version1),
@ -224,7 +227,15 @@ where
Ok(version)
}
impl TryFrom<&str> for RoomVersionId {
impl<'a> TryFrom<&'a str> for RoomVersionId<&'a str> {
type Error = crate::error::Error;
fn try_from(s: &'a str) -> Result<Self, Error> {
try_from(s)
}
}
impl TryFrom<&str> for RoomVersionId<Box<str>> {
type Error = crate::error::Error;
fn try_from(s: &str) -> Result<Self, Error> {
@ -232,7 +243,7 @@ impl TryFrom<&str> for RoomVersionId {
}
}
impl TryFrom<String> for RoomVersionId {
impl TryFrom<String> for RoomVersionId<Box<str>> {
type Error = crate::error::Error;
fn try_from(s: String) -> Result<Self, Error> {
@ -240,26 +251,26 @@ impl TryFrom<String> for RoomVersionId {
}
}
impl PartialEq<&str> for RoomVersionId {
impl<T: AsRef<str>> PartialEq<&str> for RoomVersionId<T> {
fn eq(&self, other: &&str) -> bool {
self.as_ref() == *other
}
}
impl PartialEq<RoomVersionId> for &str {
fn eq(&self, other: &RoomVersionId) -> bool {
impl<T: AsRef<str>> PartialEq<RoomVersionId<T>> for &str {
fn eq(&self, other: &RoomVersionId<T>) -> bool {
*self == other.as_ref()
}
}
impl PartialEq<String> for RoomVersionId {
impl<T: AsRef<str>> PartialEq<String> for RoomVersionId<T> {
fn eq(&self, other: &String) -> bool {
self.as_ref() == other
}
}
impl PartialEq<RoomVersionId> for String {
fn eq(&self, other: &RoomVersionId) -> bool {
impl<T: AsRef<str>> PartialEq<RoomVersionId<T>> for String {
fn eq(&self, other: &RoomVersionId<T>) -> bool {
self == other.as_ref()
}
}
@ -271,9 +282,10 @@ mod tests {
#[cfg(feature = "serde")]
use serde_json::{from_str, to_string};
use super::RoomVersionId;
use crate::error::Error;
type RoomVersionId = super::RoomVersionId<Box<str>>;
#[test]
fn valid_version_1_room_version_id() {
assert_eq!(

View File

@ -17,9 +17,9 @@ use crate::{error::Error, is_valid_server_name, parse_id};
/// "@carl:example.com"
/// );
/// ```
#[derive(Clone, Debug)]
pub struct UserId {
full_id: Box<str>,
#[derive(Clone, Copy, Debug)]
pub struct UserId<T> {
full_id: T,
colon_idx: NonZeroU8,
/// Whether this user id is a historical one.
///
@ -29,14 +29,17 @@ pub struct UserId {
is_historical: bool,
}
impl UserId {
impl<T> UserId<T> {
/// Attempts to generate a `UserId` for the given origin server with a localpart consisting of
/// 12 random ASCII characters.
///
/// Fails if the given homeserver cannot be parsed as a valid host.
#[cfg(feature = "rand")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
pub fn new(server_name: &str) -> Result<Self, Error> {
pub fn new(server_name: &str) -> Result<Self, Error>
where
String: Into<T>,
{
use crate::generate_localpart;
if !is_valid_server_name(server_name) {
@ -59,13 +62,16 @@ impl UserId {
/// localpart, not the localpart plus the `@` prefix, or the localpart plus server name without
/// the `@` prefix.
pub fn parse_with_server_name(
id: impl AsRef<str> + Into<Box<str>>,
id: impl AsRef<str> + Into<T>,
server_name: &str,
) -> Result<Self, Error> {
) -> Result<Self, Error>
where
String: Into<T>,
{
let id_str = id.as_ref();
if id_str.starts_with('@') {
try_from(id.into())
try_from(id)
} else {
let is_fully_conforming = localpart_is_fully_comforming(id_str)?;
if !is_valid_server_name(server_name) {
@ -81,13 +87,19 @@ impl UserId {
}
/// Returns the user's localpart.
pub fn localpart(&self) -> &str {
&self.full_id[1..self.colon_idx.get() as usize]
pub fn localpart(&self) -> &str
where
T: AsRef<str>,
{
&self.full_id.as_ref()[1..self.colon_idx.get() as usize]
}
/// Returns the server name of the user ID.
pub fn server_name(&self) -> &str {
&self.full_id[self.colon_idx.get() as usize + 1..]
pub fn server_name(&self) -> &str
where
T: AsRef<str>,
{
&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]
}
/// Whether this user ID is a historical one, i.e. one that doesn't conform to the latest
@ -101,9 +113,9 @@ impl UserId {
/// Attempts to create a new Matrix user ID from a string representation.
///
/// The string must include the leading @ sigil, the localpart, a literal colon, and a server name.
fn try_from<S>(user_id: S) -> Result<UserId, Error>
fn try_from<S, T>(user_id: S) -> Result<UserId<T>, Error>
where
S: AsRef<str> + Into<Box<str>>,
S: AsRef<str> + Into<T>,
{
let colon_idx = parse_id(user_id.as_ref(), &['@'])?;
let localpart = &user_id.as_ref()[1..colon_idx.get() as usize];
@ -151,9 +163,10 @@ mod tests {
#[cfg(feature = "serde")]
use serde_json::{from_str, to_string};
use super::UserId;
use crate::error::Error;
type UserId = super::UserId<Box<str>>;
#[test]
fn valid_user_id_from_str() {
let user_id = UserId::try_from("@carl:example.com").expect("Failed to create UserId.");