identifiers: Add more conversion functions

This commit is contained in:
Jonas Platte 2020-06-18 12:11:52 +02:00
parent f8492db766
commit 046668466c
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
11 changed files with 136 additions and 72 deletions

View File

@ -10,21 +10,23 @@ pub struct DeviceKeyId<T> {
colon_idx: NonZeroU8,
}
impl<T> DeviceKeyId<T> {
impl<T> DeviceKeyId<T>
where
T: AsRef<str>,
{
/// Creates a reference to this `DeviceKeyId`.
pub fn as_ref(&self) -> DeviceKeyId<&str> {
DeviceKeyId { full_id: self.full_id.as_ref(), colon_idx: self.colon_idx }
}
/// Returns key algorithm of the device key ID.
pub fn algorithm(&self) -> DeviceKeyAlgorithm
where
T: AsRef<str>,
{
pub fn algorithm(&self) -> DeviceKeyAlgorithm {
DeviceKeyAlgorithm::from_str(&self.full_id.as_ref()[..self.colon_idx.get() as usize])
.unwrap()
}
/// Returns device ID of the device key ID.
pub fn device_id(&self) -> DeviceIdRef<'_>
where
T: AsRef<str>,
{
pub fn device_id(&self) -> DeviceIdRef<'_> {
&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]
}
}

View File

@ -46,7 +46,10 @@ pub struct EventId<T> {
colon_idx: Option<NonZeroU8>,
}
impl<T> EventId<T> {
impl<T> EventId<T>
where
String: Into<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.
@ -55,24 +58,28 @@ impl<T> EventId<T> {
/// parsed as a valid host.
#[cfg(feature = "rand")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
pub fn new(server_name: ServerNameRef<'_>) -> Self
where
String: Into<T>,
{
pub fn new(server_name: ServerNameRef<'_>) -> Self {
use crate::generate_localpart;
let full_id = format!("${}:{}", generate_localpart(18), server_name).into();
Self { full_id, colon_idx: NonZeroU8::new(19) }
}
}
impl<T> EventId<T>
where
T: AsRef<str>,
{
/// Creates a reference to this `EventId`.
pub fn as_ref(&self) -> EventId<&str> {
EventId { full_id: self.full_id.as_ref(), colon_idx: self.colon_idx }
}
/// 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
where
T: AsRef<str>,
{
pub fn localpart(&self) -> &str {
let idx = match self.colon_idx {
Some(idx) => idx.get() as usize,
None => self.full_id.as_ref().len(),
@ -84,10 +91,7 @@ impl<T> EventId<T> {
/// 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<ServerNameRef<'_>>
where
T: AsRef<str>,
{
pub fn server_name(&self) -> Option<ServerNameRef<'_>> {
self.colon_idx.map(|idx| {
ServerNameRef::try_from(&self.full_id.as_ref()[idx.get() as usize + 1..]).unwrap()
})
@ -162,7 +166,7 @@ mod tests {
let server_name =
ServerNameRef::try_from("example.com").expect("Failed to parse ServerName");
let event_id = EventId::new(server_name);
let id_str: &str = event_id.as_ref();
let id_str = event_id.as_str();
assert!(id_str.starts_with('$'));
assert_eq!(id_str.len(), 31);

View File

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use strum::{AsRefStr, Display, EnumString};
/// The basic key algorithms in the specification
/// The basic key algorithms in the specification.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, AsRefStr, Display, EnumString)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
@ -23,6 +23,7 @@ pub enum DeviceKeyAlgorithm {
SignedCurve25519,
}
/// The server key algorithms defined in the Matrix spec.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, AsRefStr, Display, EnumString)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]

View File

@ -2,11 +2,7 @@
//! for events, rooms, room aliases, room versions, and users.
#![warn(rust_2018_idioms)]
#![deny(
missing_copy_implementations,
missing_debug_implementations,
//missing_docs
)]
#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs)]
// Since we support Rust 1.36.0, we can't apply this suggestion yet
#![allow(clippy::use_self)]
#![cfg_attr(docsrs, feature(doc_cfg))]

View File

@ -1,5 +1,27 @@
/// Declares an item with a doc attribute computed by some macro expression.
/// This allows documentation to be dynamically generated based on input.
/// Necessary to work around https://github.com/rust-lang/rust/issues/52607.
macro_rules! doc_concat {
( $( #[doc = $doc:expr] $thing:item )* ) => ( $( #[doc = $doc] $thing )* );
}
macro_rules! common_impls {
($id:ident, $try_from:ident, $desc:literal) => {
impl<T: ::std::convert::AsRef<str>> $id<T> {
doc_concat! {
#[doc = concat!("Creates a string slice from this `", stringify!($id), "`")]
pub fn as_str(&self) -> &str {
self.full_id.as_ref()
}
}
}
impl<'a> ::std::convert::From<&'a $id<Box<str>>> for $id<&'a str> {
fn from(id: &'a $id<Box<str>>) -> Self {
id.as_ref()
}
}
impl ::std::convert::From<$id<Box<str>>> for ::std::string::String {
fn from(id: $id<Box<str>>) -> Self {
id.full_id.into()

View File

@ -26,7 +26,15 @@ pub struct RoomAliasId<T> {
pub(crate) colon_idx: NonZeroU8,
}
impl<T: AsRef<str>> RoomAliasId<T> {
impl<T> RoomAliasId<T>
where
T: AsRef<str>,
{
/// Creates a reference to this `RoomAliasId`.
pub fn as_ref(&self) -> RoomAliasId<&str> {
RoomAliasId { full_id: self.full_id.as_ref(), colon_idx: self.colon_idx }
}
/// Returns the room's alias.
pub fn alias(&self) -> &str {
&self.full_id.as_ref()[1..self.colon_idx.get() as usize]

View File

@ -26,37 +26,41 @@ pub struct RoomId<T> {
pub(crate) colon_idx: NonZeroU8,
}
impl<T> RoomId<T> {
impl<T> RoomId<T>
where
String: Into<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: ServerNameRef<'_>) -> Self
where
String: Into<T>,
{
pub fn new(server_name: ServerNameRef<'_>) -> Self {
use crate::generate_localpart;
let full_id = format!("!{}:{}", generate_localpart(18), server_name).into();
Self { full_id, colon_idx: NonZeroU8::new(19).unwrap() }
}
}
impl<T> RoomId<T>
where
T: AsRef<str>,
{
/// Creates a reference to this `RoomId`.
pub fn as_ref(&self) -> RoomId<&str> {
RoomId { full_id: self.full_id.as_ref(), colon_idx: self.colon_idx }
}
/// Returns the rooms's unique ID.
pub fn localpart(&self) -> &str
where
T: AsRef<str>,
{
pub fn localpart(&self) -> &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) -> ServerNameRef<'_>
where
T: AsRef<str>,
{
pub fn server_name(&self) -> ServerNameRef<'_> {
ServerNameRef::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..])
.unwrap()
}
@ -103,7 +107,7 @@ mod tests {
let server_name =
ServerNameRef::try_from("example.com").expect("Failed to parse ServerName");
let room_id = RoomId::new(server_name);
let id_str: &str = room_id.as_ref();
let id_str = room_id.as_str();
assert!(id_str.starts_with('!'));
assert_eq!(id_str.len(), 31);

View File

@ -34,7 +34,15 @@ pub struct RoomIdOrAliasId<T> {
colon_idx: NonZeroU8,
}
impl<T: AsRef<str>> RoomIdOrAliasId<T> {
impl<T> RoomIdOrAliasId<T>
where
T: AsRef<str>,
{
/// Creates a reference to this `RoomIdOrAliasId`.
pub fn as_ref(&self) -> RoomIdOrAliasId<&str> {
RoomIdOrAliasId { full_id: self.full_id.as_ref(), colon_idx: self.colon_idx }
}
/// Returns the local part (everything after the `!` or `#` and before the first colon).
pub fn localpart(&self) -> &str {
&self.full_id.as_ref()[1..self.colon_idx.get() as usize]

View File

@ -11,21 +11,23 @@ pub struct ServerKeyId<T> {
colon_idx: NonZeroU8,
}
impl<T> ServerKeyId<T> {
impl<T> ServerKeyId<T>
where
T: AsRef<str>,
{
/// Creates a reference to this `ServerKeyId`.
pub fn as_ref(&self) -> ServerKeyId<&str> {
ServerKeyId { full_id: self.full_id.as_ref(), colon_idx: self.colon_idx }
}
/// Returns key algorithm of the server key ID.
pub fn algorithm(&self) -> ServerKeyAlgorithm
where
T: AsRef<str>,
{
pub fn algorithm(&self) -> ServerKeyAlgorithm {
ServerKeyAlgorithm::from_str(&self.full_id.as_ref()[..self.colon_idx.get() as usize])
.unwrap()
}
/// Returns the version of the server key ID.
pub fn version(&self) -> &str
where
T: AsRef<str>,
{
pub fn version(&self) -> &str {
&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]
}
}

View File

@ -11,6 +11,16 @@ pub struct ServerName<T> {
full_id: T,
}
impl<T> ServerName<T>
where
T: AsRef<str>,
{
/// Creates a reference to this `ServerName`.
pub fn as_ref(&self) -> ServerName<&str> {
ServerName { full_id: self.full_id.as_ref() }
}
}
fn try_from<S, T>(server_name: S) -> Result<ServerName<T>, Error>
where
S: AsRef<str> + Into<T>,

View File

@ -32,15 +32,15 @@ pub struct UserId<T> {
is_historical: bool,
}
impl<T> UserId<T> {
impl<T> UserId<T>
where
String: Into<T>,
{
/// Attempts to generate a `UserId` for the given origin server with a localpart consisting of
/// 12 random ASCII characters.
#[cfg(feature = "rand")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
pub fn new(server_name: ServerNameRef<'_>) -> Self
where
String: Into<T>,
{
pub fn new(server_name: ServerNameRef<'_>) -> Self {
use crate::generate_localpart;
let full_id = format!("@{}:{}", generate_localpart(12).to_lowercase(), server_name).into();
@ -58,10 +58,7 @@ impl<T> UserId<T> {
pub fn parse_with_server_name(
id: impl AsRef<str> + Into<T>,
server_name: ServerNameRef<'_>,
) -> Result<Self, Error>
where
String: Into<T>,
{
) -> Result<Self, Error> {
let id_str = id.as_ref();
if id_str.starts_with('@') {
@ -76,24 +73,34 @@ impl<T> UserId<T> {
})
}
}
}
impl<T> UserId<T>
where
T: AsRef<str>,
{
/// Creates a reference to this `UserId`.
pub fn as_ref(&self) -> UserId<&str> {
UserId {
full_id: self.full_id.as_ref(),
colon_idx: self.colon_idx,
is_historical: self.is_historical,
}
}
/// Returns the user's localpart.
pub fn localpart(&self) -> &str
where
T: AsRef<str>,
{
pub fn localpart(&self) -> &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) -> ServerNameRef<'_>
where
T: AsRef<str>,
{
pub fn server_name(&self) -> ServerNameRef<'_> {
ServerNameRef::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..])
.unwrap()
}
}
impl<T> UserId<T> {
/// Whether this user ID is a historical one, i.e. one that doesn't conform to the latest
/// specification of the user ID grammar but is still accepted because it was previously
/// allowed.
@ -232,7 +239,7 @@ mod tests {
assert_eq!(user_id.localpart().len(), 12);
assert_eq!(user_id.server_name(), "example.com");
let id_str: &str = user_id.as_ref();
let id_str = user_id.as_str();
assert!(id_str.starts_with('@'));
assert_eq!(id_str.len(), 25);