Revert making identifier types generic over the underlying string type
At the same time, this commit makes `ServerName` a newtype around str so other identifier types can borrow out their server name part as a `&ServerName`. This technique works for `ServerName` because it keeps no additional metadata. For the other identifier types to support being created in borrowed form from a string slice, custom DSTs first have to be added to Rust.
This commit is contained in:
parent
8683901e14
commit
a3e5d679a1
@ -35,7 +35,7 @@ ruma_api! {
|
||||
/// If this does not correspond to a known client device, a new device will be created.
|
||||
/// The server will auto-generate a device_id if this is not specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub device_id: Option<DeviceId>,
|
||||
pub device_id: Option<Box<DeviceId>>,
|
||||
|
||||
/// A display name to assign to the newly-created device.
|
||||
///
|
||||
@ -78,7 +78,7 @@ ruma_api! {
|
||||
/// ID of the registered device.
|
||||
///
|
||||
/// Will be the same as the corresponding parameter in the request, if one was specified.
|
||||
pub device_id: Option<DeviceId>,
|
||||
pub device_id: Option<Box<DeviceId>>,
|
||||
}
|
||||
|
||||
error: UiaaResponse
|
||||
|
@ -15,7 +15,7 @@ pub mod update_device;
|
||||
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
|
||||
pub struct Device {
|
||||
/// Device ID
|
||||
pub device_id: DeviceId,
|
||||
pub device_id: Box<DeviceId>,
|
||||
|
||||
/// Public display name of the device.
|
||||
pub display_name: Option<String>,
|
||||
|
@ -18,7 +18,7 @@ ruma_api! {
|
||||
request: {
|
||||
/// The device to delete.
|
||||
#[ruma_api(path)]
|
||||
pub device_id: DeviceId,
|
||||
pub device_id: Box<DeviceId>,
|
||||
|
||||
/// Additional authentication information for the user-interactive authentication API.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -17,7 +17,7 @@ ruma_api! {
|
||||
|
||||
request: {
|
||||
/// List of devices to delete.
|
||||
pub devices: Vec<DeviceId>,
|
||||
pub devices: Vec<Box<DeviceId>>,
|
||||
|
||||
/// Additional authentication information for the user-interactive authentication API.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -17,7 +17,7 @@ ruma_api! {
|
||||
request: {
|
||||
/// The device to retrieve.
|
||||
#[ruma_api(path)]
|
||||
pub device_id: DeviceId,
|
||||
pub device_id: Box<DeviceId>,
|
||||
}
|
||||
|
||||
response: {
|
||||
|
@ -16,7 +16,7 @@ ruma_api! {
|
||||
request: {
|
||||
/// The device to update.
|
||||
#[ruma_api(path)]
|
||||
pub device_id: DeviceId,
|
||||
pub device_id: Box<DeviceId>,
|
||||
|
||||
/// The new display name for this device. If this is `None`, the display name won't be
|
||||
/// changed.
|
||||
|
@ -60,7 +60,7 @@ impl TryFrom<&'_ str> for KeyAlgorithm {
|
||||
|
||||
/// A key algorithm and a device id, combined with a ':'
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct AlgorithmAndDeviceId(pub KeyAlgorithm, pub DeviceId);
|
||||
pub struct AlgorithmAndDeviceId(pub KeyAlgorithm, pub Box<DeviceId>);
|
||||
|
||||
impl Display for AlgorithmAndDeviceId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
@ -102,7 +102,7 @@ impl<'de> Deserialize<'de> for AlgorithmAndDeviceId {
|
||||
|
||||
let algorithm_result = KeyAlgorithm::try_from(parts[0]);
|
||||
match algorithm_result {
|
||||
Ok(algorithm) => Ok(AlgorithmAndDeviceId(algorithm, parts[1].to_string())),
|
||||
Ok(algorithm) => Ok(AlgorithmAndDeviceId(algorithm, parts[1].into())),
|
||||
Err(_) => {
|
||||
Err(de::Error::invalid_value(Unexpected::Str(parts[0]), &"valid key algorithm"))
|
||||
}
|
||||
@ -117,7 +117,7 @@ pub struct DeviceKeys {
|
||||
pub user_id: UserId,
|
||||
|
||||
/// The ID of the device these keys belong to. Must match the device ID used when logging in.
|
||||
pub device_id: DeviceId,
|
||||
pub device_id: Box<DeviceId>,
|
||||
|
||||
/// The encryption algorithms supported by this device.
|
||||
pub algorithms: Vec<Algorithm>,
|
||||
|
@ -31,7 +31,7 @@ ruma_api! {
|
||||
pub timeout: Option<Duration>,
|
||||
|
||||
/// The keys to be claimed.
|
||||
pub one_time_keys: BTreeMap<UserId, BTreeMap<DeviceId, KeyAlgorithm>>,
|
||||
pub one_time_keys: BTreeMap<UserId, BTreeMap<Box<DeviceId>, KeyAlgorithm>>,
|
||||
}
|
||||
|
||||
response: {
|
||||
@ -40,8 +40,11 @@ ruma_api! {
|
||||
pub failures: BTreeMap<String, JsonValue>,
|
||||
|
||||
/// One-time keys for the queried devices.
|
||||
pub one_time_keys: BTreeMap<UserId, BTreeMap<DeviceId, BTreeMap<AlgorithmAndDeviceId, OneTimeKey>>>,
|
||||
pub one_time_keys: BTreeMap<UserId, OneTimeKeys>,
|
||||
}
|
||||
|
||||
error: crate::Error
|
||||
}
|
||||
|
||||
/// The one-time keys for a given device.
|
||||
pub type OneTimeKeys = BTreeMap<Box<DeviceId>, BTreeMap<AlgorithmAndDeviceId, OneTimeKey>>;
|
||||
|
@ -30,7 +30,7 @@ ruma_api! {
|
||||
|
||||
/// The keys to be downloaded. An empty list indicates all devices for
|
||||
/// the corresponding user.
|
||||
pub device_keys: BTreeMap<UserId, Vec<DeviceId>>,
|
||||
pub device_keys: BTreeMap<UserId, Vec<Box<DeviceId>>>,
|
||||
|
||||
/// If the client is fetching keys as a result of a device update
|
||||
/// received in a sync request, this should be the 'since' token of that
|
||||
@ -48,7 +48,7 @@ ruma_api! {
|
||||
pub failures: BTreeMap<String, JsonValue>,
|
||||
|
||||
/// Information on the queried devices.
|
||||
pub device_keys: BTreeMap<UserId, BTreeMap<DeviceId, DeviceKeys>>,
|
||||
pub device_keys: BTreeMap<UserId, BTreeMap<Box<DeviceId>, DeviceKeys>>,
|
||||
}
|
||||
|
||||
error: crate::Error
|
||||
|
@ -27,7 +27,7 @@ ruma_api! {
|
||||
|
||||
/// ID of the client device
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub device_id: Option<DeviceId>,
|
||||
pub device_id: Option<Box<DeviceId>>,
|
||||
|
||||
/// A display name to assign to the newly-created device. Ignored if device_id corresponds
|
||||
/// to a known device.
|
||||
|
@ -17,7 +17,7 @@ pub mod send_event_to_device;
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum DeviceIdOrAllDevices {
|
||||
/// Represents a device Id for one of a user's devices.
|
||||
DeviceId(DeviceId),
|
||||
DeviceId(Box<DeviceId>),
|
||||
|
||||
/// Represents all devices for a user.
|
||||
AllDevices,
|
||||
@ -40,7 +40,7 @@ impl TryFrom<&str> for DeviceIdOrAllDevices {
|
||||
} else if "*" == device_id_or_all_devices {
|
||||
Ok(DeviceIdOrAllDevices::AllDevices)
|
||||
} else {
|
||||
Ok(DeviceIdOrAllDevices::DeviceId(device_id_or_all_devices.to_string()))
|
||||
Ok(DeviceIdOrAllDevices::DeviceId(device_id_or_all_devices.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ impl DerefMut for DirectEventContent {
|
||||
mod tests {
|
||||
use std::{collections::BTreeMap, convert::TryFrom};
|
||||
|
||||
use ruma_identifiers::{RoomId, ServerNameRef, UserId};
|
||||
use ruma_identifiers::{RoomId, ServerName, UserId};
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
use super::{DirectEvent, DirectEventContent};
|
||||
@ -47,7 +47,7 @@ mod tests {
|
||||
#[test]
|
||||
fn serialization() {
|
||||
let mut content = DirectEventContent(BTreeMap::new());
|
||||
let server_name = ServerNameRef::try_from("ruma.io").unwrap();
|
||||
let server_name = <&ServerName>::try_from("ruma.io").unwrap();
|
||||
let alice = UserId::new(server_name);
|
||||
let room = vec![RoomId::new(server_name)];
|
||||
|
||||
@ -66,7 +66,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn deserialization() {
|
||||
let server_name = ServerNameRef::try_from("ruma.io").unwrap();
|
||||
let server_name = <&ServerName>::try_from("ruma.io").unwrap();
|
||||
let alice = UserId::new(server_name);
|
||||
let rooms = vec![RoomId::new(server_name), RoomId::new(server_name)];
|
||||
|
||||
|
@ -19,7 +19,7 @@ pub type RequestEvent = BasicEvent<RequestEventContent>;
|
||||
#[ruma_event(type = "m.key.verification.request")]
|
||||
pub struct RequestEventContent {
|
||||
/// The device ID which is initiating the request.
|
||||
pub from_device: DeviceId,
|
||||
pub from_device: Box<DeviceId>,
|
||||
|
||||
/// An opaque identifier for the verification request.
|
||||
///
|
||||
|
@ -29,7 +29,7 @@ pub enum StartEventContent {
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct MSasV1Content {
|
||||
/// The device ID which is initiating the process.
|
||||
pub(crate) from_device: DeviceId,
|
||||
pub(crate) from_device: Box<DeviceId>,
|
||||
|
||||
/// An opaque identifier for the verification process.
|
||||
///
|
||||
@ -63,7 +63,7 @@ pub struct MSasV1Content {
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct MSasV1ContentOptions {
|
||||
/// The device ID which is initiating the process.
|
||||
pub from_device: DeviceId,
|
||||
pub from_device: Box<DeviceId>,
|
||||
|
||||
/// An opaque identifier for the verification process.
|
||||
///
|
||||
@ -270,7 +270,7 @@ mod tests {
|
||||
key_agreement_protocols,
|
||||
message_authentication_codes,
|
||||
short_authentication_string,
|
||||
}) if from_device == "123"
|
||||
}) if from_device.as_ref() == "123"
|
||||
&& transaction_id == "456"
|
||||
&& hashes == vec![HashAlgorithm::Sha256]
|
||||
&& key_agreement_protocols == vec![KeyAgreementProtocol::Curve25519]
|
||||
@ -305,7 +305,7 @@ mod tests {
|
||||
message_authentication_codes,
|
||||
short_authentication_string,
|
||||
})
|
||||
} if from_device == "123"
|
||||
} if from_device.as_ref() == "123"
|
||||
&& transaction_id == "456"
|
||||
&& hashes == vec![HashAlgorithm::Sha256]
|
||||
&& key_agreement_protocols == vec![KeyAgreementProtocol::Curve25519]
|
||||
|
@ -60,7 +60,7 @@ pub struct MegolmV1AesSha2Content {
|
||||
pub sender_key: String,
|
||||
|
||||
/// The ID of the sending device.
|
||||
pub device_id: DeviceId,
|
||||
pub device_id: Box<DeviceId>,
|
||||
|
||||
/// The ID of the session used to encrypt the message.
|
||||
pub session_id: String,
|
||||
@ -117,7 +117,7 @@ mod tests {
|
||||
session_id,
|
||||
}) if ciphertext == "ciphertext"
|
||||
&& sender_key == "sender_key"
|
||||
&& device_id == "device_id"
|
||||
&& device_id.as_ref() == "device_id"
|
||||
&& session_id == "session_id"
|
||||
);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ mod tests {
|
||||
time::{Duration, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use ruma_identifiers::{EventId, RoomId, ServerNameRef, UserId};
|
||||
use ruma_identifiers::{EventId, RoomId, ServerName, UserId};
|
||||
use serde_json::to_string;
|
||||
|
||||
use super::PinnedEventsEventContent;
|
||||
@ -33,7 +33,7 @@ mod tests {
|
||||
#[test]
|
||||
fn serialization_deserialization() {
|
||||
let mut content: PinnedEventsEventContent = PinnedEventsEventContent { pinned: Vec::new() };
|
||||
let server_name = ServerNameRef::try_from("example.com").unwrap();
|
||||
let server_name = <&ServerName>::try_from("example.com").unwrap();
|
||||
|
||||
content.pinned.push(EventId::new(server_name));
|
||||
content.pinned.push(EventId::new(server_name));
|
||||
|
@ -26,7 +26,7 @@ pub struct RoomKeyRequestEventContent {
|
||||
pub body: Option<RequestedKeyInfo>,
|
||||
|
||||
/// ID of the device requesting the key.
|
||||
pub requesting_device_id: DeviceId,
|
||||
pub requesting_device_id: Box<DeviceId>,
|
||||
|
||||
/// A random string uniquely identifying the request for a key.
|
||||
///
|
||||
|
@ -7,12 +7,12 @@ use crate::generate_localpart;
|
||||
///
|
||||
/// Device identifiers in Matrix are completely opaque character sequences. This type alias is
|
||||
/// provided simply for its semantic value.
|
||||
pub type DeviceId = String;
|
||||
pub type DeviceId = str;
|
||||
|
||||
/// Generates a random `DeviceId`, suitable for assignment to a new device.
|
||||
#[cfg(feature = "rand")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
|
||||
pub fn generate() -> DeviceId {
|
||||
pub fn generate() -> Box<DeviceId> {
|
||||
generate_localpart(8)
|
||||
}
|
||||
|
||||
|
@ -1,24 +1,16 @@
|
||||
//! Identifiers for device keys for end-to-end encryption.
|
||||
|
||||
use crate::{error::Error, key_algorithms::DeviceKeyAlgorithm, DeviceIdRef};
|
||||
use crate::{error::Error, key_algorithms::DeviceKeyAlgorithm, DeviceId};
|
||||
use std::{num::NonZeroU8, str::FromStr};
|
||||
|
||||
/// A key algorithm and a device id, combined with a ':'
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeviceKeyId<T> {
|
||||
full_id: T,
|
||||
pub struct DeviceKeyId {
|
||||
full_id: Box<str>,
|
||||
colon_idx: NonZeroU8,
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
impl DeviceKeyId {
|
||||
/// Returns key algorithm of the device key ID.
|
||||
pub fn algorithm(&self) -> DeviceKeyAlgorithm {
|
||||
DeviceKeyAlgorithm::from_str(&self.full_id.as_ref()[..self.colon_idx.get() as usize])
|
||||
@ -26,14 +18,14 @@ where
|
||||
}
|
||||
|
||||
/// Returns device ID of the device key ID.
|
||||
pub fn device_id(&self) -> DeviceIdRef<'_> {
|
||||
pub fn device_id(&self) -> &DeviceId {
|
||||
&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]
|
||||
}
|
||||
}
|
||||
|
||||
fn try_from<S, T>(key_id: S) -> Result<DeviceKeyId<T>, Error>
|
||||
fn try_from<S>(key_id: S) -> Result<DeviceKeyId, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<T>,
|
||||
S: AsRef<str> + Into<Box<str>>,
|
||||
{
|
||||
let key_str = key_id.as_ref();
|
||||
let colon_idx =
|
||||
@ -56,12 +48,12 @@ mod test {
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
use super::DeviceKeyId;
|
||||
use crate::{device_id::DeviceId, error::Error, key_algorithms::DeviceKeyAlgorithm};
|
||||
use crate::{error::Error, key_algorithms::DeviceKeyAlgorithm};
|
||||
|
||||
#[test]
|
||||
fn convert_device_key_id() {
|
||||
assert_eq!(
|
||||
DeviceKeyId::<&str>::try_from("ed25519:JLAFKJWSCS")
|
||||
DeviceKeyId::try_from("ed25519:JLAFKJWSCS")
|
||||
.expect("Failed to create device key ID.")
|
||||
.as_ref(),
|
||||
"ed25519:JLAFKJWSCS"
|
||||
@ -71,7 +63,7 @@ mod test {
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn serialize_device_key_id() {
|
||||
let device_key_id = DeviceKeyId::<&str>::try_from("ed25519:JLAFKJWSCS").unwrap();
|
||||
let device_key_id = DeviceKeyId::try_from("ed25519:JLAFKJWSCS").unwrap();
|
||||
let serialized = to_json_value(device_key_id).unwrap();
|
||||
|
||||
let expected = json!("ed25519:JLAFKJWSCS");
|
||||
@ -81,7 +73,7 @@ mod test {
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn deserialize_device_key_id() {
|
||||
let deserialized: DeviceKeyId<_> = from_json_value(json!("ed25519:JLAFKJWSCS")).unwrap();
|
||||
let deserialized: DeviceKeyId = from_json_value(json!("ed25519:JLAFKJWSCS")).unwrap();
|
||||
|
||||
let expected = DeviceKeyId::try_from("ed25519:JLAFKJWSCS").unwrap();
|
||||
assert_eq!(deserialized, expected);
|
||||
@ -89,16 +81,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn missing_key_algorithm() {
|
||||
assert_eq!(
|
||||
DeviceKeyId::<&str>::try_from(":JLAFKJWSCS").unwrap_err(),
|
||||
Error::UnknownKeyAlgorithm
|
||||
);
|
||||
assert_eq!(DeviceKeyId::try_from(":JLAFKJWSCS").unwrap_err(), Error::UnknownKeyAlgorithm);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_delimiter() {
|
||||
assert_eq!(
|
||||
DeviceKeyId::<&str>::try_from("ed25519|JLAFKJWSCS").unwrap_err(),
|
||||
DeviceKeyId::try_from("ed25519|JLAFKJWSCS").unwrap_err(),
|
||||
Error::MissingDeviceKeyDelimiter,
|
||||
);
|
||||
}
|
||||
@ -106,25 +95,25 @@ mod test {
|
||||
#[test]
|
||||
fn unknown_key_algorithm() {
|
||||
assert_eq!(
|
||||
DeviceKeyId::<&str>::try_from("signed_curve25510:JLAFKJWSCS").unwrap_err(),
|
||||
DeviceKeyId::try_from("signed_curve25510:JLAFKJWSCS").unwrap_err(),
|
||||
Error::UnknownKeyAlgorithm,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_device_id_ok() {
|
||||
assert!(DeviceKeyId::<&str>::try_from("ed25519:").is_ok());
|
||||
assert!(DeviceKeyId::try_from("ed25519:").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_key_algorithm() {
|
||||
let device_key_id = DeviceKeyId::<&str>::try_from("ed25519:JLAFKJWSCS").unwrap();
|
||||
let device_key_id = DeviceKeyId::try_from("ed25519:JLAFKJWSCS").unwrap();
|
||||
assert_eq!(device_key_id.algorithm(), DeviceKeyAlgorithm::Ed25519);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_device_id() {
|
||||
let device_key_id = DeviceKeyId::<&str>::try_from("ed25519:JLAFKJWSCS").unwrap();
|
||||
assert_eq!(device_key_id.device_id(), DeviceId::from("JLAFKJWSCS"));
|
||||
let device_key_id = DeviceKeyId::try_from("ed25519:JLAFKJWSCS").unwrap();
|
||||
assert_eq!(device_key_id.device_id(), "JLAFKJWSCS");
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,13 @@
|
||||
|
||||
use std::{convert::TryFrom, num::NonZeroU8};
|
||||
|
||||
use crate::{error::Error, parse_id, validate_id, ServerNameRef};
|
||||
use crate::{error::Error, parse_id, validate_id, ServerName};
|
||||
|
||||
/// A Matrix event ID.
|
||||
///
|
||||
/// An `EventId` is generated randomly or converted from a string slice, and can be converted back
|
||||
/// into a string as needed.
|
||||
///
|
||||
/// It is discouraged to use this type directly – instead use one of the aliases (`EventId` and
|
||||
/// `EventIdRef`) in the crate root.
|
||||
///
|
||||
/// # Room versions
|
||||
///
|
||||
/// Matrix specifies multiple [room versions](https://matrix.org/docs/spec/#room-versions) and the
|
||||
@ -40,16 +37,13 @@ use crate::{error::Error, parse_id, validate_id, ServerNameRef};
|
||||
/// "$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg"
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct EventId<T> {
|
||||
full_id: T,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EventId {
|
||||
full_id: Box<str>,
|
||||
colon_idx: Option<NonZeroU8>,
|
||||
}
|
||||
|
||||
impl<T> EventId<T>
|
||||
where
|
||||
String: Into<T>,
|
||||
{
|
||||
impl EventId {
|
||||
/// 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.
|
||||
@ -58,7 +52,7 @@ where
|
||||
/// parsed as a valid host.
|
||||
#[cfg(feature = "rand")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
|
||||
pub fn new(server_name: ServerNameRef<'_>) -> Self {
|
||||
pub fn new(server_name: &ServerName) -> Self {
|
||||
use crate::generate_localpart;
|
||||
|
||||
let full_id = format!("${}:{}", generate_localpart(18), server_name).into();
|
||||
@ -67,33 +61,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
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 {
|
||||
let idx = match self.colon_idx {
|
||||
Some(idx) => idx.get() as usize,
|
||||
None => self.full_id.as_ref().len(),
|
||||
None => self.full_id.len(),
|
||||
};
|
||||
|
||||
&self.full_id.as_ref()[1..idx]
|
||||
&self.full_id[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<ServerNameRef<'_>> {
|
||||
pub fn server_name(&self) -> Option<&ServerName> {
|
||||
self.colon_idx.map(|idx| {
|
||||
ServerNameRef::try_from(&self.full_id.as_ref()[idx.get() as usize + 1..]).unwrap()
|
||||
<&ServerName>::try_from(&self.full_id.as_ref()[idx.get() as usize + 1..]).unwrap()
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -102,9 +88,9 @@ where
|
||||
///
|
||||
/// 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, T>(event_id: S) -> Result<EventId<T>, Error>
|
||||
fn try_from<S>(event_id: S) -> Result<EventId, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<T>,
|
||||
S: AsRef<str> + Into<Box<str>>,
|
||||
{
|
||||
if event_id.as_ref().contains(':') {
|
||||
let colon_idx = parse_id(event_id.as_ref(), &['$'])?;
|
||||
@ -126,9 +112,8 @@ mod tests {
|
||||
#[cfg(feature = "serde")]
|
||||
use serde_json::{from_str, to_string};
|
||||
|
||||
use crate::{error::Error, ServerNameRef};
|
||||
|
||||
type EventId = super::EventId<Box<str>>;
|
||||
use super::EventId;
|
||||
use crate::{error::Error, ServerName};
|
||||
|
||||
#[test]
|
||||
fn valid_original_event_id() {
|
||||
@ -164,7 +149,7 @@ mod tests {
|
||||
#[test]
|
||||
fn generate_random_valid_event_id() {
|
||||
let server_name =
|
||||
ServerNameRef::try_from("example.com").expect("Failed to parse ServerName");
|
||||
<&ServerName>::try_from("example.com").expect("Failed to parse ServerName");
|
||||
let event_id = EventId::new(server_name);
|
||||
let id_str = event_id.as_str();
|
||||
|
||||
|
@ -13,152 +13,44 @@ use std::{convert::TryFrom, num::NonZeroU8};
|
||||
use serde::de::{self, Deserialize as _, Deserializer, Unexpected};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::error::Error;
|
||||
pub use crate::{
|
||||
device_id::DeviceId,
|
||||
device_key_id::DeviceKeyId,
|
||||
error::Error,
|
||||
event_id::EventId,
|
||||
key_algorithms::{DeviceKeyAlgorithm, ServerKeyAlgorithm},
|
||||
room_alias_id::RoomAliasId,
|
||||
room_id::RoomId,
|
||||
room_id_or_room_alias_id::RoomIdOrAliasId,
|
||||
room_version_id::RoomVersionId,
|
||||
server_key_id::ServerKeyId,
|
||||
server_name::ServerName,
|
||||
user_id::UserId,
|
||||
};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod error;
|
||||
|
||||
pub mod device_id;
|
||||
pub mod device_key_id;
|
||||
pub mod event_id;
|
||||
pub mod key_algorithms;
|
||||
pub mod room_alias_id;
|
||||
pub mod room_id;
|
||||
pub mod room_id_or_room_alias_id;
|
||||
pub mod room_version_id;
|
||||
pub mod server_key_id;
|
||||
#[allow(deprecated)]
|
||||
pub mod server_name;
|
||||
pub mod user_id;
|
||||
|
||||
/// Allowed algorithms for homeserver signing keys.
|
||||
pub type DeviceKeyAlgorithm = key_algorithms::DeviceKeyAlgorithm;
|
||||
|
||||
/// An owned device key identifier containing a key algorithm and device ID.
|
||||
///
|
||||
/// Can be created via `TryFrom<String>` and `TryFrom<&str>`; implements `Serialize`
|
||||
/// and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type DeviceKeyId = device_key_id::DeviceKeyId<Box<str>>;
|
||||
|
||||
/// A reference to a device key identifier containing a key algorithm and device ID.
|
||||
///
|
||||
/// Can be created via `TryFrom<&str>`; implements `Serialize` and `Deserialize`
|
||||
/// if the `serde` feature is enabled.
|
||||
pub type DeviceKeyIdRef<'a> = device_key_id::DeviceKeyId<&'a str>;
|
||||
|
||||
/// An owned device ID.
|
||||
///
|
||||
/// While this is currently just a `String`, that will likely change in the future.
|
||||
pub use device_id::DeviceId;
|
||||
|
||||
/// A reference to a device ID.
|
||||
///
|
||||
/// While this is currently just a string slice, that will likely change in the future.
|
||||
pub type DeviceIdRef<'a> = &'a str;
|
||||
|
||||
/// An owned event ID.
|
||||
///
|
||||
/// Can be created via `new` (if the `rand` feature is enabled) and `TryFrom<String>` +
|
||||
/// `TryFrom<&str>`, implements `Serialize` and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type EventId = event_id::EventId<Box<str>>;
|
||||
|
||||
/// A reference to an event ID.
|
||||
///
|
||||
/// Can be created via `TryFrom<&str>`, implements `Serialize` if the `serde` feature is enabled.
|
||||
pub type EventIdRef<'a> = event_id::EventId<&'a str>;
|
||||
|
||||
/// An owned room alias ID.
|
||||
///
|
||||
/// Can be created via `TryFrom<String>` and `TryFrom<&str>`, implements `Serialize` and
|
||||
/// `Deserialize` if the `serde` feature is enabled.
|
||||
pub type RoomAliasId = room_alias_id::RoomAliasId<Box<str>>;
|
||||
|
||||
/// A reference to a room alias ID.
|
||||
///
|
||||
/// Can be created via `TryFrom<&str>`, implements `Serialize` if the `serde` feature is enabled.
|
||||
pub type RoomAliasIdRef<'a> = room_alias_id::RoomAliasId<&'a str>;
|
||||
|
||||
/// An owned room ID.
|
||||
///
|
||||
/// Can be created via `new` (if the `rand` feature is enabled) and `TryFrom<String>` +
|
||||
/// `TryFrom<&str>`, implements `Serialize` and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type RoomId = room_id::RoomId<Box<str>>;
|
||||
|
||||
/// A reference to a room ID.
|
||||
///
|
||||
/// Can be created via `TryFrom<&str>`, implements `Serialize` if the `serde` feature is enabled.
|
||||
pub type RoomIdRef<'a> = room_id::RoomId<&'a str>;
|
||||
|
||||
/// An owned room alias ID or room ID.
|
||||
///
|
||||
/// Can be created via `TryFrom<String>`, `TryFrom<&str>`, `From<RoomId>` and `From<RoomAliasId>`;
|
||||
/// implements `Serialize` and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type RoomIdOrAliasId = room_id_or_room_alias_id::RoomIdOrAliasId<Box<str>>;
|
||||
|
||||
/// A reference to a room alias ID or room ID.
|
||||
///
|
||||
/// Can be created via `TryFrom<&str>`, `From<RoomIdRef>` and `From<RoomAliasIdRef>`; implements
|
||||
/// `Serialize` if the `serde` feature is enabled.
|
||||
pub type RoomIdOrAliasIdRef<'a> = room_id_or_room_alias_id::RoomIdOrAliasId<&'a str>;
|
||||
|
||||
/// An owned room version ID.
|
||||
///
|
||||
/// Can be created using the `version_N` constructor functions, `TryFrom<String>` and
|
||||
/// `TryFrom<&str>`; implements `Serialize` and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type RoomVersionId = room_version_id::RoomVersionId<Box<str>>;
|
||||
|
||||
/// A reference to a room version ID.
|
||||
///
|
||||
/// Can be created using the `version_N` constructor functions and via `TryFrom<&str>`, implements
|
||||
/// `Serialize` if the `serde` feature is enabled.
|
||||
pub type RoomVersionIdRef<'a> = room_version_id::RoomVersionId<&'a str>;
|
||||
|
||||
/// Allowed algorithms for homeserver signing keys.
|
||||
pub type ServerKeyAlgorithm = key_algorithms::ServerKeyAlgorithm;
|
||||
|
||||
/// An owned homeserver signing key identifier containing a key algorithm and version.
|
||||
///
|
||||
/// Can be created via `TryFrom<String>` and `TryFrom<&str>`; implements `Serialize`
|
||||
/// and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type ServerKeyId = server_key_id::ServerKeyId<Box<str>>;
|
||||
|
||||
/// A reference to a homeserver signing key identifier containing a key
|
||||
/// algorithm and version.
|
||||
///
|
||||
/// Can be created via `TryFrom<&str>`; implements `Serialize`
|
||||
/// and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type ServerKeyIdRef<'a> = server_key_id::ServerKeyId<&'a str>;
|
||||
|
||||
/// An owned homeserver IP address or hostname.
|
||||
///
|
||||
/// Can be created via `TryFrom<String>` and `TryFrom<&str>`; implements `Serialize`
|
||||
/// and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type ServerName = server_name::ServerName<Box<str>>;
|
||||
|
||||
/// A reference to a homeserver IP address or hostname.
|
||||
///
|
||||
/// Can be created via `TryFrom<&str>`; implements `Serialize`
|
||||
/// and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type ServerNameRef<'a> = server_name::ServerName<&'a str>;
|
||||
/// An owned user ID.
|
||||
///
|
||||
/// Can be created via `new` (if the `rand` feature is enabled) and `TryFrom<String>` +
|
||||
/// `TryFrom<&str>`, implements `Serialize` and `Deserialize` if the `serde` feature is enabled.
|
||||
pub type UserId = user_id::UserId<Box<str>>;
|
||||
|
||||
/// A reference to a user ID.
|
||||
///
|
||||
/// Can be created via `TryFrom<&str>`, implements `Serialize` if the `serde` feature is enabled.
|
||||
pub type UserIdRef<'a> = user_id::UserId<&'a str>;
|
||||
mod device_key_id;
|
||||
mod error;
|
||||
mod event_id;
|
||||
mod key_algorithms;
|
||||
mod room_alias_id;
|
||||
mod room_id;
|
||||
mod room_id_or_room_alias_id;
|
||||
mod room_version_id;
|
||||
mod server_key_id;
|
||||
mod server_name;
|
||||
|
||||
/// Check whether a given string is a valid server name according to [the specification][].
|
||||
///
|
||||
/// [the specification]: https://matrix.org/docs/spec/appendices#server-name
|
||||
#[deprecated = "Use the [`ServerName`](server_name/struct.ServerName.html) type instead."]
|
||||
pub fn is_valid_server_name(name: &str) -> bool {
|
||||
ServerNameRef::try_from(name).is_ok()
|
||||
<&ServerName>::try_from(name).is_ok()
|
||||
}
|
||||
|
||||
/// All identifiers must be 255 bytes or less.
|
||||
@ -171,9 +63,13 @@ const MIN_CHARS: usize = 4;
|
||||
|
||||
/// Generates a random identifier localpart.
|
||||
#[cfg(feature = "rand")]
|
||||
fn generate_localpart(length: usize) -> String {
|
||||
fn generate_localpart(length: usize) -> Box<str> {
|
||||
use rand::Rng as _;
|
||||
rand::thread_rng().sample_iter(&rand::distributions::Alphanumeric).take(length).collect()
|
||||
rand::thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
.take(length)
|
||||
.collect::<String>()
|
||||
.into_boxed_str()
|
||||
}
|
||||
|
||||
/// Checks if an identifier is valid.
|
||||
@ -203,7 +99,7 @@ fn parse_id(id: &str, valid_sigils: &[char]) -> Result<NonZeroU8, Error> {
|
||||
return Err(Error::InvalidLocalPart);
|
||||
}
|
||||
|
||||
server_name::ServerName::<&str>::try_from(&id[colon_idx + 1..])?;
|
||||
server_name::validate(&id[colon_idx + 1..])?;
|
||||
|
||||
Ok(NonZeroU8::new(colon_idx as u8).unwrap())
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ macro_rules! doc_concat {
|
||||
}
|
||||
|
||||
macro_rules! common_impls {
|
||||
($id:ident, $try_from:ident, $desc:literal) => {
|
||||
impl<T: ::std::convert::AsRef<str>> $id<T> {
|
||||
($id:ty, $try_from:ident, $desc:literal) => {
|
||||
impl $id {
|
||||
doc_concat! {
|
||||
#[doc = concat!("Creates a string slice from this `", stringify!($id), "`")]
|
||||
pub fn as_str(&self) -> &str {
|
||||
@ -16,27 +16,19 @@ macro_rules! common_impls {
|
||||
}
|
||||
}
|
||||
|
||||
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::AsRef<str> for $id {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::From<$id<Box<str>>> for ::std::string::String {
|
||||
fn from(id: $id<Box<str>>) -> Self {
|
||||
impl ::std::convert::From<$id> for ::std::string::String {
|
||||
fn from(id: $id) -> Self {
|
||||
id.full_id.into()
|
||||
}
|
||||
}
|
||||
|
||||
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>> {
|
||||
impl ::std::convert::TryFrom<&str> for $id {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
@ -44,7 +36,7 @@ macro_rules! common_impls {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<String> for $id<Box<str>> {
|
||||
impl ::std::convert::TryFrom<String> for $id {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
@ -52,56 +44,50 @@ macro_rules! common_impls {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::std::convert::AsRef<str>> ::std::convert::AsRef<str> for $id<T> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.full_id.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::std::fmt::Display> ::std::fmt::Display for $id<T> {
|
||||
impl ::std::fmt::Display for $id {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "{}", self.full_id)
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::std::cmp::PartialEq> ::std::cmp::PartialEq for $id<T> {
|
||||
impl ::std::cmp::PartialEq for $id {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.full_id == other.full_id
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::std::cmp::Eq> ::std::cmp::Eq for $id<T> {}
|
||||
impl ::std::cmp::Eq for $id {}
|
||||
|
||||
impl<T: ::std::cmp::PartialOrd> ::std::cmp::PartialOrd for $id<T> {
|
||||
impl ::std::cmp::PartialOrd for $id {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
|
||||
::std::cmp::PartialOrd::partial_cmp(&self.full_id, &other.full_id)
|
||||
::std::cmp::PartialOrd::partial_cmp(self.as_str(), other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::std::cmp::Ord> ::std::cmp::Ord for $id<T> {
|
||||
impl ::std::cmp::Ord for $id {
|
||||
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
|
||||
::std::cmp::Ord::cmp(&self.full_id, &other.full_id)
|
||||
::std::cmp::Ord::cmp(self.as_str(), other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::std::hash::Hash> ::std::hash::Hash for $id<T> {
|
||||
impl ::std::hash::Hash for $id {
|
||||
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.full_id.hash(state);
|
||||
self.as_str().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T: AsRef<str>> ::serde::Serialize for $id<T> {
|
||||
impl ::serde::Serialize for $id {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ::serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.full_id.as_ref())
|
||||
serializer.serialize_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> ::serde::Deserialize<'de> for $id<Box<str>> {
|
||||
impl<'de> ::serde::Deserialize<'de> for $id {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: ::serde::Deserializer<'de>,
|
||||
@ -110,27 +96,27 @@ macro_rules! common_impls {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ::std::cmp::PartialEq<&str> for $id<T> {
|
||||
impl ::std::cmp::PartialEq<&str> for $id {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.full_id.as_ref() == *other
|
||||
self.as_str() == *other
|
||||
}
|
||||
}
|
||||
|
||||
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<$id> for &str {
|
||||
fn eq(&self, other: &$id) -> bool {
|
||||
*self == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ::std::cmp::PartialEq<::std::string::String> for $id<T> {
|
||||
impl ::std::cmp::PartialEq<::std::string::String> for $id {
|
||||
fn eq(&self, other: &::std::string::String) -> bool {
|
||||
self.full_id.as_ref() == &other[..]
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
impl ::std::cmp::PartialEq<$id> for ::std::string::String {
|
||||
fn eq(&self, other: &$id) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -6,9 +6,6 @@ use crate::{error::Error, parse_id, server_name::ServerName};
|
||||
|
||||
/// A Matrix room alias ID.
|
||||
///
|
||||
/// It is discouraged to use this type directly – instead use one of the aliases (`RoomAliasId` and
|
||||
/// `RoomAliasIdRef`) in the crate root.
|
||||
///
|
||||
/// A `RoomAliasId` is converted from a string slice, and can be converted back into a string as
|
||||
/// needed.
|
||||
///
|
||||
@ -20,38 +17,30 @@ use crate::{error::Error, parse_id, server_name::ServerName};
|
||||
/// "#ruma:example.com"
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct RoomAliasId<T> {
|
||||
pub(crate) full_id: T,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoomAliasId {
|
||||
pub(crate) full_id: Box<str>,
|
||||
pub(crate) colon_idx: NonZeroU8,
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
impl RoomAliasId {
|
||||
/// Returns the room's alias.
|
||||
pub fn alias(&self) -> &str {
|
||||
&self.full_id.as_ref()[1..self.colon_idx.get() as usize]
|
||||
&self.full_id[1..self.colon_idx.get() as usize]
|
||||
}
|
||||
|
||||
/// Returns the server name of the room alias ID.
|
||||
pub fn server_name(&self) -> ServerName<&str> {
|
||||
ServerName::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]).unwrap()
|
||||
pub fn server_name(&self) -> &ServerName {
|
||||
<&ServerName>::try_from(&self.full_id[self.colon_idx.get() as usize + 1..]).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, T>(room_id: S) -> Result<RoomAliasId<T>, Error>
|
||||
fn try_from<S>(room_id: S) -> Result<RoomAliasId, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<T>,
|
||||
S: AsRef<str> + Into<Box<str>>,
|
||||
{
|
||||
let colon_idx = parse_id(room_id.as_ref(), &['#'])?;
|
||||
|
||||
@ -67,10 +56,9 @@ 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!(
|
||||
|
@ -2,16 +2,13 @@
|
||||
|
||||
use std::{convert::TryFrom, num::NonZeroU8};
|
||||
|
||||
use crate::{error::Error, parse_id, ServerNameRef};
|
||||
use crate::{error::Error, parse_id, ServerName};
|
||||
|
||||
/// A Matrix room ID.
|
||||
///
|
||||
/// A `RoomId` is generated randomly or converted from a string slice, and can be converted back
|
||||
/// into a string as needed.
|
||||
///
|
||||
/// It is discouraged to use this type directly – instead use one of the aliases (`RoomId` and
|
||||
/// `RoomIdRef`) in the crate root.
|
||||
///
|
||||
/// ```
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # use ruma_identifiers::RoomId;
|
||||
@ -20,23 +17,20 @@ use crate::{error::Error, parse_id, ServerNameRef};
|
||||
/// "!n8f893n9:example.com"
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct RoomId<T> {
|
||||
pub(crate) full_id: T,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoomId {
|
||||
pub(crate) full_id: Box<str>,
|
||||
pub(crate) colon_idx: NonZeroU8,
|
||||
}
|
||||
|
||||
impl<T> RoomId<T>
|
||||
where
|
||||
String: Into<T>,
|
||||
{
|
||||
impl RoomId {
|
||||
/// 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 {
|
||||
pub fn new(server_name: &ServerName) -> Self {
|
||||
use crate::generate_localpart;
|
||||
|
||||
let full_id = format!("!{}:{}", generate_localpart(18), server_name).into();
|
||||
@ -45,33 +39,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
impl RoomId {
|
||||
/// Returns the rooms's unique ID.
|
||||
pub fn localpart(&self) -> &str {
|
||||
&self.full_id.as_ref()[1..self.colon_idx.get() as usize]
|
||||
&self.full_id[1..self.colon_idx.get() as usize]
|
||||
}
|
||||
|
||||
/// Returns the server name of the room ID.
|
||||
pub fn server_name(&self) -> ServerNameRef<'_> {
|
||||
ServerNameRef::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..])
|
||||
.unwrap()
|
||||
pub fn server_name(&self) -> &ServerName {
|
||||
<&ServerName>::try_from(&self.full_id[self.colon_idx.get() as usize + 1..]).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, T>(room_id: S) -> Result<RoomId<T>, Error>
|
||||
fn try_from<S>(room_id: S) -> Result<RoomId, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<T>,
|
||||
S: AsRef<str> + Into<Box<str>>,
|
||||
{
|
||||
let colon_idx = parse_id(room_id.as_ref(), &['!'])?;
|
||||
|
||||
@ -87,9 +72,8 @@ mod tests {
|
||||
#[cfg(feature = "serde")]
|
||||
use serde_json::{from_str, to_string};
|
||||
|
||||
use crate::{error::Error, ServerNameRef};
|
||||
|
||||
type RoomId = super::RoomId<Box<str>>;
|
||||
use super::RoomId;
|
||||
use crate::{error::Error, ServerName};
|
||||
|
||||
#[test]
|
||||
fn valid_room_id() {
|
||||
@ -105,7 +89,7 @@ mod tests {
|
||||
#[test]
|
||||
fn generate_random_valid_room_id() {
|
||||
let server_name =
|
||||
ServerNameRef::try_from("example.com").expect("Failed to parse ServerName");
|
||||
<&ServerName>::try_from("example.com").expect("Failed to parse ServerName");
|
||||
let room_id = RoomId::new(server_name);
|
||||
let id_str = room_id.as_str();
|
||||
|
||||
|
@ -2,9 +2,7 @@
|
||||
|
||||
use std::{convert::TryFrom, hint::unreachable_unchecked, num::NonZeroU8};
|
||||
|
||||
use crate::{
|
||||
error::Error, parse_id, room_alias_id::RoomAliasId, room_id::RoomId, server_name::ServerName,
|
||||
};
|
||||
use crate::{error::Error, parse_id, server_name::ServerName, RoomAliasId, RoomId};
|
||||
|
||||
/// A Matrix room ID or a Matrix room alias ID.
|
||||
///
|
||||
@ -12,9 +10,6 @@ use crate::{
|
||||
/// 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.
|
||||
///
|
||||
/// It is discouraged to use this type directly – instead use one of the aliases
|
||||
/// (`RoomIdOrRoomAliasId` and `RoomIdOrRoomAliasIdRef`) in the crate root.
|
||||
///
|
||||
/// ```
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # use ruma_identifiers::RoomIdOrAliasId;
|
||||
@ -28,29 +23,21 @@ use crate::{
|
||||
/// "!n8f893n9:example.com"
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct RoomIdOrAliasId<T> {
|
||||
full_id: T,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoomIdOrAliasId {
|
||||
full_id: Box<str>,
|
||||
colon_idx: NonZeroU8,
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
impl RoomIdOrAliasId {
|
||||
/// 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]
|
||||
&self.full_id[1..self.colon_idx.get() as usize]
|
||||
}
|
||||
|
||||
/// Returns the server name of the room (alias) ID.
|
||||
pub fn server_name(&self) -> ServerName<&str> {
|
||||
ServerName::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]).unwrap()
|
||||
pub fn server_name(&self) -> &ServerName {
|
||||
<&ServerName>::try_from(&self.full_id[self.colon_idx.get() as usize + 1..]).unwrap()
|
||||
}
|
||||
|
||||
/// Whether this is a room id (starts with `'!'`)
|
||||
@ -66,7 +53,7 @@ where
|
||||
/// 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<T>, RoomAliasId<T>> {
|
||||
pub fn into_either(self) -> either::Either<RoomId, RoomAliasId> {
|
||||
match self.variant() {
|
||||
Variant::RoomId => {
|
||||
either::Either::Left(RoomId { full_id: self.full_id, colon_idx: self.colon_idx })
|
||||
@ -79,7 +66,7 @@ where
|
||||
}
|
||||
|
||||
fn variant(&self) -> Variant {
|
||||
match self.full_id.as_ref().bytes().next() {
|
||||
match self.full_id.bytes().next() {
|
||||
Some(b'!') => Variant::RoomId,
|
||||
Some(b'#') => Variant::RoomAliasId,
|
||||
_ => unsafe { unreachable_unchecked() },
|
||||
@ -98,9 +85,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, T>(room_id_or_alias_id: S) -> Result<RoomIdOrAliasId<T>, Error>
|
||||
fn try_from<S>(room_id_or_alias_id: S) -> Result<RoomIdOrAliasId, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<T>,
|
||||
S: AsRef<str> + Into<Box<str>>,
|
||||
{
|
||||
let colon_idx = parse_id(room_id_or_alias_id.as_ref(), &['#', '!'])?;
|
||||
Ok(RoomIdOrAliasId { full_id: room_id_or_alias_id.into(), colon_idx })
|
||||
@ -108,22 +95,22 @@ where
|
||||
|
||||
common_impls!(RoomIdOrAliasId, try_from, "a Matrix room ID or room alias ID");
|
||||
|
||||
impl<T> From<RoomId<T>> for RoomIdOrAliasId<T> {
|
||||
fn from(RoomId { full_id, colon_idx }: RoomId<T>) -> Self {
|
||||
impl From<RoomId> for RoomIdOrAliasId {
|
||||
fn from(RoomId { full_id, colon_idx }: RoomId) -> Self {
|
||||
Self { full_id, colon_idx }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RoomAliasId<T>> for RoomIdOrAliasId<T> {
|
||||
fn from(RoomAliasId { full_id, colon_idx }: RoomAliasId<T>) -> Self {
|
||||
impl From<RoomAliasId> for RoomIdOrAliasId {
|
||||
fn from(RoomAliasId { full_id, colon_idx }: RoomAliasId) -> Self {
|
||||
Self { full_id, colon_idx }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> TryFrom<RoomIdOrAliasId<T>> for RoomId<T> {
|
||||
type Error = RoomAliasId<T>;
|
||||
impl TryFrom<RoomIdOrAliasId> for RoomId {
|
||||
type Error = RoomAliasId;
|
||||
|
||||
fn try_from(id: RoomIdOrAliasId<T>) -> Result<RoomId<T>, RoomAliasId<T>> {
|
||||
fn try_from(id: RoomIdOrAliasId) -> Result<RoomId, RoomAliasId> {
|
||||
match id.variant() {
|
||||
Variant::RoomId => Ok(RoomId { full_id: id.full_id, colon_idx: id.colon_idx }),
|
||||
Variant::RoomAliasId => {
|
||||
@ -133,10 +120,10 @@ impl<T: AsRef<str>> TryFrom<RoomIdOrAliasId<T>> for RoomId<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> TryFrom<RoomIdOrAliasId<T>> for RoomAliasId<T> {
|
||||
type Error = RoomId<T>;
|
||||
impl TryFrom<RoomIdOrAliasId> for RoomAliasId {
|
||||
type Error = RoomId;
|
||||
|
||||
fn try_from(id: RoomIdOrAliasId<T>) -> Result<RoomAliasId<T>, RoomId<T>> {
|
||||
fn try_from(id: RoomIdOrAliasId) -> Result<RoomAliasId, RoomId> {
|
||||
match id.variant() {
|
||||
Variant::RoomAliasId => {
|
||||
Ok(RoomAliasId { full_id: id.full_id, colon_idx: id.colon_idx })
|
||||
@ -153,10 +140,9 @@ 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!(
|
||||
|
@ -19,21 +19,18 @@ const MAX_CODE_POINTS: usize = 32;
|
||||
/// A `RoomVersionId` can be or converted or deserialized from a string slice, and can be converted
|
||||
/// or serialized back into a string as needed.
|
||||
///
|
||||
/// It is discouraged to use this type directly – instead use one of the aliases (`RoomVersionId`
|
||||
/// and `RoomVersionIdRef`) in the crate root.
|
||||
///
|
||||
/// ```
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # use ruma_identifiers::RoomVersionId;
|
||||
/// assert_eq!(RoomVersionId::try_from("1").unwrap().as_ref(), "1");
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct RoomVersionId<T>(InnerRoomVersionId<T>);
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct RoomVersionId(InnerRoomVersionId);
|
||||
|
||||
/// Possibile values for room version, distinguishing between official Matrix versions and custom
|
||||
/// versions.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
enum InnerRoomVersionId<T> {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
enum InnerRoomVersionId {
|
||||
/// A version 1 room.
|
||||
Version1,
|
||||
|
||||
@ -53,10 +50,10 @@ enum InnerRoomVersionId<T> {
|
||||
Version6,
|
||||
|
||||
/// A custom room version.
|
||||
Custom(T),
|
||||
Custom(Box<str>),
|
||||
}
|
||||
|
||||
impl<T> RoomVersionId<T> {
|
||||
impl RoomVersionId {
|
||||
/// Creates a version 1 room ID.
|
||||
pub fn version_1() -> Self {
|
||||
Self(InnerRoomVersionId::Version1)
|
||||
@ -102,37 +99,37 @@ impl<T> RoomVersionId<T> {
|
||||
|
||||
/// Whether or not this is a version 1 room.
|
||||
pub fn is_version_1(&self) -> bool {
|
||||
matches!(self.0, InnerRoomVersionId::Version1)
|
||||
self.0 == InnerRoomVersionId::Version1
|
||||
}
|
||||
|
||||
/// Whether or not this is a version 2 room.
|
||||
pub fn is_version_2(&self) -> bool {
|
||||
matches!(self.0, InnerRoomVersionId::Version2)
|
||||
self.0 == InnerRoomVersionId::Version2
|
||||
}
|
||||
|
||||
/// Whether or not this is a version 3 room.
|
||||
pub fn is_version_3(&self) -> bool {
|
||||
matches!(self.0, InnerRoomVersionId::Version3)
|
||||
self.0 == InnerRoomVersionId::Version3
|
||||
}
|
||||
|
||||
/// Whether or not this is a version 4 room.
|
||||
pub fn is_version_4(&self) -> bool {
|
||||
matches!(self.0, InnerRoomVersionId::Version4)
|
||||
self.0 == InnerRoomVersionId::Version4
|
||||
}
|
||||
|
||||
/// Whether or not this is a version 5 room.
|
||||
pub fn is_version_5(&self) -> bool {
|
||||
matches!(self.0, InnerRoomVersionId::Version5)
|
||||
self.0 == InnerRoomVersionId::Version5
|
||||
}
|
||||
|
||||
/// Whether or not this is a version 6 room.
|
||||
pub fn is_version_6(&self) -> bool {
|
||||
matches!(self.0, InnerRoomVersionId::Version5)
|
||||
self.0 == InnerRoomVersionId::Version5
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RoomVersionId<Box<str>>> for String {
|
||||
fn from(id: RoomVersionId<Box<str>>) -> Self {
|
||||
impl From<RoomVersionId> for String {
|
||||
fn from(id: RoomVersionId) -> Self {
|
||||
match id.0 {
|
||||
InnerRoomVersionId::Version1 => "1".to_owned(),
|
||||
InnerRoomVersionId::Version2 => "2".to_owned(),
|
||||
@ -145,7 +142,7 @@ impl From<RoomVersionId<Box<str>>> for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> AsRef<str> for RoomVersionId<T> {
|
||||
impl AsRef<str> for RoomVersionId {
|
||||
fn as_ref(&self) -> &str {
|
||||
match &self.0 {
|
||||
InnerRoomVersionId::Version1 => "1",
|
||||
@ -154,31 +151,31 @@ impl<T: AsRef<str>> AsRef<str> for RoomVersionId<T> {
|
||||
InnerRoomVersionId::Version4 => "4",
|
||||
InnerRoomVersionId::Version5 => "5",
|
||||
InnerRoomVersionId::Version6 => "6",
|
||||
InnerRoomVersionId::Custom(version) => version.as_ref(),
|
||||
InnerRoomVersionId::Custom(version) => version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> Display for RoomVersionId<T> {
|
||||
impl Display for RoomVersionId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq + AsRef<str>> PartialOrd for RoomVersionId<T> {
|
||||
fn partial_cmp(&self, other: &RoomVersionId<T>) -> Option<Ordering> {
|
||||
impl PartialOrd for RoomVersionId {
|
||||
fn partial_cmp(&self, other: &RoomVersionId) -> Option<Ordering> {
|
||||
self.as_ref().partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq + AsRef<str>> Ord for RoomVersionId<T> {
|
||||
impl Ord for RoomVersionId {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_ref().cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T: AsRef<str>> Serialize for RoomVersionId<T> {
|
||||
impl Serialize for RoomVersionId {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@ -188,7 +185,7 @@ impl<T: AsRef<str>> Serialize for RoomVersionId<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> Deserialize<'de> for RoomVersionId<Box<str>> {
|
||||
impl<'de> Deserialize<'de> for RoomVersionId {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
@ -198,9 +195,9 @@ impl<'de> Deserialize<'de> for RoomVersionId<Box<str>> {
|
||||
}
|
||||
|
||||
/// Attempts to create a new Matrix room version ID from a string representation.
|
||||
fn try_from<S, T>(room_version_id: S) -> Result<RoomVersionId<T>, Error>
|
||||
fn try_from<S>(room_version_id: S) -> Result<RoomVersionId, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<T>,
|
||||
S: AsRef<str> + Into<Box<str>>,
|
||||
{
|
||||
let version = match room_version_id.as_ref() {
|
||||
"1" => RoomVersionId(InnerRoomVersionId::Version1),
|
||||
@ -222,15 +219,7 @@ where
|
||||
Ok(version)
|
||||
}
|
||||
|
||||
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>> {
|
||||
impl TryFrom<&str> for RoomVersionId {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Error> {
|
||||
@ -238,7 +227,7 @@ impl TryFrom<&str> for RoomVersionId<Box<str>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for RoomVersionId<Box<str>> {
|
||||
impl TryFrom<String> for RoomVersionId {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Error> {
|
||||
@ -246,26 +235,26 @@ impl TryFrom<String> for RoomVersionId<Box<str>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> PartialEq<&str> for RoomVersionId<T> {
|
||||
impl PartialEq<&str> for RoomVersionId {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.as_ref() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> PartialEq<RoomVersionId<T>> for &str {
|
||||
fn eq(&self, other: &RoomVersionId<T>) -> bool {
|
||||
impl PartialEq<RoomVersionId> for &str {
|
||||
fn eq(&self, other: &RoomVersionId) -> bool {
|
||||
*self == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> PartialEq<String> for RoomVersionId<T> {
|
||||
impl PartialEq<String> for RoomVersionId {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_ref() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> PartialEq<RoomVersionId<T>> for String {
|
||||
fn eq(&self, other: &RoomVersionId<T>) -> bool {
|
||||
impl PartialEq<RoomVersionId> for String {
|
||||
fn eq(&self, other: &RoomVersionId) -> bool {
|
||||
self == other.as_ref()
|
||||
}
|
||||
}
|
||||
@ -277,10 +266,9 @@ 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!(
|
||||
|
@ -6,20 +6,12 @@ use crate::{error::Error, key_algorithms::ServerKeyAlgorithm};
|
||||
|
||||
/// Key identifiers used for homeserver signing keys.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ServerKeyId<T> {
|
||||
full_id: T,
|
||||
pub struct ServerKeyId {
|
||||
full_id: Box<str>,
|
||||
colon_idx: NonZeroU8,
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
impl ServerKeyId {
|
||||
/// Returns key algorithm of the server key ID.
|
||||
pub fn algorithm(&self) -> ServerKeyAlgorithm {
|
||||
ServerKeyAlgorithm::from_str(&self.full_id.as_ref()[..self.colon_idx.get() as usize])
|
||||
@ -32,9 +24,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn try_from<S, T>(key_id: S) -> Result<ServerKeyId<T>, Error>
|
||||
fn try_from<S>(key_id: S) -> Result<ServerKeyId, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<T>,
|
||||
S: AsRef<str> + Into<Box<str>>,
|
||||
{
|
||||
let key_str = key_id.as_ref();
|
||||
let colon_idx =
|
||||
@ -83,7 +75,7 @@ mod tests {
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn deserialize_id() {
|
||||
let server_key_id: ServerKeyId<_> = from_json_value(json!("ed25519:Abc_1")).unwrap();
|
||||
let server_key_id: ServerKeyId = from_json_value(json!("ed25519:Abc_1")).unwrap();
|
||||
assert_eq!(server_key_id.algorithm(), ServerKeyAlgorithm::Ed25519);
|
||||
assert_eq!(server_key_id.version(), "Abc_1");
|
||||
}
|
||||
@ -91,22 +83,19 @@ mod tests {
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn serialize_id() {
|
||||
let server_key_id: ServerKeyId<&str> = ServerKeyId::try_from("ed25519:abc123").unwrap();
|
||||
let server_key_id: ServerKeyId = ServerKeyId::try_from("ed25519:abc123").unwrap();
|
||||
assert_eq!(to_json_value(&server_key_id).unwrap(), json!("ed25519:abc123"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_version_characters() {
|
||||
assert_eq!(
|
||||
ServerKeyId::<&str>::try_from("ed25519:Abc-1").unwrap_err(),
|
||||
Error::InvalidCharacters,
|
||||
);
|
||||
assert_eq!(ServerKeyId::try_from("ed25519:Abc-1").unwrap_err(), Error::InvalidCharacters,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_key_algorithm() {
|
||||
assert_eq!(
|
||||
ServerKeyId::<&str>::try_from("signed_curve25519:Abc-1").unwrap_err(),
|
||||
ServerKeyId::try_from("signed_curve25519:Abc-1").unwrap_err(),
|
||||
Error::UnknownKeyAlgorithm,
|
||||
);
|
||||
}
|
||||
@ -114,7 +103,7 @@ mod tests {
|
||||
#[test]
|
||||
fn missing_delimiter() {
|
||||
assert_eq!(
|
||||
ServerKeyId::<&str>::try_from("ed25519|Abc_1").unwrap_err(),
|
||||
ServerKeyId::try_from("ed25519|Abc_1").unwrap_err(),
|
||||
Error::MissingServerKeyDelimiter,
|
||||
);
|
||||
}
|
||||
|
@ -1,53 +1,75 @@
|
||||
//! Matrix-spec compliant server names.
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
fmt::{self, Display},
|
||||
mem,
|
||||
};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
/// A Matrix-spec compliant server name.
|
||||
///
|
||||
/// It is discouraged to use this type directly – instead use one of the aliases ([`ServerName`](../type.ServerName.html) and
|
||||
/// [`ServerNameRef`](../type.ServerNameRef.html)) in the crate root.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ServerName<T> {
|
||||
full_id: T,
|
||||
}
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
|
||||
pub struct ServerName(str);
|
||||
|
||||
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() }
|
||||
impl ServerName {
|
||||
#[allow(clippy::transmute_ptr_to_ptr)]
|
||||
fn from_borrowed(s: &str) -> &Self {
|
||||
unsafe { mem::transmute(s) }
|
||||
}
|
||||
|
||||
fn from_owned(s: Box<str>) -> Box<Self> {
|
||||
unsafe { mem::transmute(s) }
|
||||
}
|
||||
|
||||
fn into_owned(self: Box<Self>) -> Box<str> {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
|
||||
/// Creates a string slice from this `ServerName`.
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn try_from<S, T>(server_name: S) -> Result<ServerName<T>, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<T>,
|
||||
{
|
||||
impl Clone for Box<ServerName> {
|
||||
fn clone(&self) -> Self {
|
||||
(**self).to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for ServerName {
|
||||
type Owned = Box<ServerName>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
Self::from_owned(self.0.to_owned().into_boxed_str())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn validate(server_name: &str) -> Result<(), Error> {
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
let name = server_name.as_ref();
|
||||
|
||||
if name.is_empty() {
|
||||
if server_name.is_empty() {
|
||||
return Err(Error::InvalidServerName);
|
||||
}
|
||||
|
||||
let end_of_host = if name.starts_with('[') {
|
||||
let end_of_ipv6 = match name.find(']') {
|
||||
let end_of_host = if server_name.starts_with('[') {
|
||||
let end_of_ipv6 = match server_name.find(']') {
|
||||
Some(idx) => idx,
|
||||
None => return Err(Error::InvalidServerName),
|
||||
};
|
||||
|
||||
if name[1..end_of_ipv6].parse::<Ipv6Addr>().is_err() {
|
||||
if server_name[1..end_of_ipv6].parse::<Ipv6Addr>().is_err() {
|
||||
return Err(Error::InvalidServerName);
|
||||
}
|
||||
|
||||
end_of_ipv6 + 1
|
||||
} else {
|
||||
let end_of_host = name.find(':').unwrap_or_else(|| name.len());
|
||||
let end_of_host = server_name.find(':').unwrap_or_else(|| server_name.len());
|
||||
|
||||
if name[..end_of_host]
|
||||
if server_name[..end_of_host]
|
||||
.bytes()
|
||||
.any(|byte| !(byte.is_ascii_alphanumeric() || byte == b'-' || byte == b'.'))
|
||||
{
|
||||
@ -57,81 +79,170 @@ where
|
||||
end_of_host
|
||||
};
|
||||
|
||||
if name.len() != end_of_host
|
||||
if server_name.len() != end_of_host
|
||||
&& (
|
||||
// hostname is followed by something other than ":port"
|
||||
name.as_bytes()[end_of_host] != b':'
|
||||
server_name.as_bytes()[end_of_host] != b':'
|
||||
// the remaining characters after ':' are not a valid port
|
||||
|| name[end_of_host + 1..].parse::<u16>().is_err()
|
||||
|| server_name[end_of_host + 1..].parse::<u16>().is_err()
|
||||
)
|
||||
{
|
||||
Err(Error::InvalidServerName)
|
||||
} else {
|
||||
Ok(ServerName { full_id: server_name.into() })
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
common_impls!(ServerName, try_from, "An IP address or hostname");
|
||||
fn try_from<S>(server_name: S) -> Result<Box<ServerName>, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<Box<str>>,
|
||||
{
|
||||
validate(server_name.as_ref())?;
|
||||
Ok(ServerName::from_owned(server_name.into()))
|
||||
}
|
||||
|
||||
impl AsRef<str> for ServerName {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Box<ServerName> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<ServerName>> for String {
|
||||
fn from(id: Box<ServerName>) -> Self {
|
||||
id.into_owned().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for &'a ServerName {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(server_name: &'a str) -> Result<Self, Self::Error> {
|
||||
validate(server_name)?;
|
||||
Ok(ServerName::from_borrowed(server_name))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Box<ServerName> {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Box<ServerName> {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ServerName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> serde::Deserialize<'de> for Box<ServerName> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
crate::deserialize_id(deserializer, "An IP address or hostname")
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for ServerName {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<ServerName> for str {
|
||||
fn eq(&self, other: &ServerName) -> bool {
|
||||
self == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for ServerName {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<ServerName> for String {
|
||||
fn eq(&self, other: &ServerName) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::ServerNameRef;
|
||||
use super::ServerName;
|
||||
|
||||
#[test]
|
||||
fn ipv4_host() {
|
||||
assert!(ServerNameRef::try_from("127.0.0.1").is_ok());
|
||||
assert!(<&ServerName>::try_from("127.0.0.1").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv4_host_and_port() {
|
||||
assert!(ServerNameRef::try_from("1.1.1.1:12000").is_ok());
|
||||
assert!(<&ServerName>::try_from("1.1.1.1:12000").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv6() {
|
||||
assert!(ServerNameRef::try_from("[::1]").is_ok());
|
||||
assert!(<&ServerName>::try_from("[::1]").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv6_with_port() {
|
||||
assert!(ServerNameRef::try_from("[1234:5678::abcd]:5678").is_ok());
|
||||
assert!(<&ServerName>::try_from("[1234:5678::abcd]:5678").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dns_name() {
|
||||
assert!(ServerNameRef::try_from("example.com").is_ok());
|
||||
assert!(<&ServerName>::try_from("example.com").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dns_name_with_port() {
|
||||
assert!(ServerNameRef::try_from("ruma.io:8080").is_ok());
|
||||
assert!(<&ServerName>::try_from("ruma.io:8080").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_string() {
|
||||
assert!(ServerNameRef::try_from("").is_err());
|
||||
assert!(<&ServerName>::try_from("").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_ipv6() {
|
||||
assert!(ServerNameRef::try_from("[test::1]").is_err());
|
||||
assert!(<&ServerName>::try_from("[test::1]").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv4_with_invalid_port() {
|
||||
assert!(ServerNameRef::try_from("127.0.0.1:").is_err());
|
||||
assert!(<&ServerName>::try_from("127.0.0.1:").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv6_with_invalid_port() {
|
||||
assert!(ServerNameRef::try_from("[fe80::1]:100000").is_err());
|
||||
assert!(ServerNameRef::try_from("[fe80::1]!").is_err());
|
||||
assert!(<&ServerName>::try_from("[fe80::1]:100000").is_err());
|
||||
assert!(<&ServerName>::try_from("[fe80::1]!").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dns_name_with_invalid_port() {
|
||||
assert!(ServerNameRef::try_from("matrix.org:hello").is_err());
|
||||
assert!(<&ServerName>::try_from("matrix.org:hello").is_err());
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,13 @@
|
||||
|
||||
use std::{convert::TryFrom, num::NonZeroU8};
|
||||
|
||||
use crate::{error::Error, parse_id, ServerNameRef};
|
||||
use crate::{error::Error, parse_id, ServerName};
|
||||
|
||||
/// A Matrix user ID.
|
||||
///
|
||||
/// A `UserId` is generated randomly or converted from a string slice, and can be converted back
|
||||
/// into a string as needed.
|
||||
///
|
||||
/// It is discouraged to use this type directly – instead use one of the aliases (`UserId` and
|
||||
/// `UserIdRef`) in the crate root.
|
||||
///
|
||||
/// ```
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # use ruma_identifiers::UserId;
|
||||
@ -20,9 +17,9 @@ use crate::{error::Error, parse_id, ServerNameRef};
|
||||
/// "@carl:example.com"
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct UserId<T> {
|
||||
full_id: T,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserId {
|
||||
full_id: Box<str>,
|
||||
colon_idx: NonZeroU8,
|
||||
/// Whether this user id is a historical one.
|
||||
///
|
||||
@ -32,15 +29,12 @@ pub struct UserId<T> {
|
||||
is_historical: bool,
|
||||
}
|
||||
|
||||
impl<T> UserId<T>
|
||||
where
|
||||
String: Into<T>,
|
||||
{
|
||||
impl UserId {
|
||||
/// 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 {
|
||||
pub fn new(server_name: &ServerName) -> Self {
|
||||
use crate::generate_localpart;
|
||||
|
||||
let full_id = format!("@{}:{}", generate_localpart(12).to_lowercase(), server_name).into();
|
||||
@ -56,13 +50,13 @@ where
|
||||
/// 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<T>,
|
||||
server_name: ServerNameRef<'_>,
|
||||
id: impl AsRef<str> + Into<Box<str>>,
|
||||
server_name: &ServerName,
|
||||
) -> Result<Self, Error> {
|
||||
let id_str = id.as_ref();
|
||||
|
||||
if id_str.starts_with('@') {
|
||||
try_from(id)
|
||||
try_from(id.into())
|
||||
} else {
|
||||
let is_fully_conforming = localpart_is_fully_comforming(id_str)?;
|
||||
|
||||
@ -75,32 +69,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
impl UserId {
|
||||
/// Returns the user's localpart.
|
||||
pub fn localpart(&self) -> &str {
|
||||
&self.full_id.as_ref()[1..self.colon_idx.get() as usize]
|
||||
&self.full_id[1..self.colon_idx.get() as usize]
|
||||
}
|
||||
|
||||
/// Returns the server name of the user ID.
|
||||
pub fn server_name(&self) -> ServerNameRef<'_> {
|
||||
ServerNameRef::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..])
|
||||
pub fn server_name(&self) -> &ServerName {
|
||||
<&ServerName>::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.
|
||||
@ -112,9 +92,9 @@ impl<T> UserId<T> {
|
||||
/// 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, T>(user_id: S) -> Result<UserId<T>, Error>
|
||||
fn try_from<S>(user_id: S) -> Result<UserId, Error>
|
||||
where
|
||||
S: AsRef<str> + Into<T>,
|
||||
S: AsRef<str> + Into<Box<str>>,
|
||||
{
|
||||
let colon_idx = parse_id(user_id.as_ref(), &['@'])?;
|
||||
let localpart = &user_id.as_ref()[1..colon_idx.get() as usize];
|
||||
@ -158,9 +138,8 @@ mod tests {
|
||||
#[cfg(feature = "serde")]
|
||||
use serde_json::{from_str, to_string};
|
||||
|
||||
use crate::{error::Error, ServerNameRef};
|
||||
|
||||
type UserId = super::UserId<Box<str>>;
|
||||
use super::UserId;
|
||||
use crate::{error::Error, ServerName};
|
||||
|
||||
#[test]
|
||||
fn valid_user_id_from_str() {
|
||||
@ -173,7 +152,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_valid_user_id() {
|
||||
let server_name = ServerNameRef::try_from("example.com").unwrap();
|
||||
let server_name = <&ServerName>::try_from("example.com").unwrap();
|
||||
let user_id = UserId::parse_with_server_name("@carl:example.com", server_name)
|
||||
.expect("Failed to create UserId.");
|
||||
assert_eq!(user_id.as_ref(), "@carl:example.com");
|
||||
@ -184,7 +163,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_valid_user_id_parts() {
|
||||
let server_name = ServerNameRef::try_from("example.com").unwrap();
|
||||
let server_name = <&ServerName>::try_from("example.com").unwrap();
|
||||
let user_id =
|
||||
UserId::parse_with_server_name("carl", server_name).expect("Failed to create UserId.");
|
||||
assert_eq!(user_id.as_ref(), "@carl:example.com");
|
||||
@ -204,7 +183,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_valid_historical_user_id() {
|
||||
let server_name = ServerNameRef::try_from("example.com").unwrap();
|
||||
let server_name = <&ServerName>::try_from("example.com").unwrap();
|
||||
let user_id = UserId::parse_with_server_name("@a%b[irc]:example.com", server_name)
|
||||
.expect("Failed to create UserId.");
|
||||
assert_eq!(user_id.as_ref(), "@a%b[irc]:example.com");
|
||||
@ -215,7 +194,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_valid_historical_user_id_parts() {
|
||||
let server_name = ServerNameRef::try_from("example.com").unwrap();
|
||||
let server_name = <&ServerName>::try_from("example.com").unwrap();
|
||||
let user_id = UserId::parse_with_server_name("a%b[irc]", server_name)
|
||||
.expect("Failed to create UserId.");
|
||||
assert_eq!(user_id.as_ref(), "@a%b[irc]:example.com");
|
||||
@ -234,7 +213,7 @@ mod tests {
|
||||
#[cfg(feature = "rand")]
|
||||
#[test]
|
||||
fn generate_random_valid_user_id() {
|
||||
let server_name = ServerNameRef::try_from("example.com").unwrap();
|
||||
let server_name = <&ServerName>::try_from("example.com").unwrap();
|
||||
let user_id = UserId::new(server_name);
|
||||
assert_eq!(user_id.localpart().len(), 12);
|
||||
assert_eq!(user_id.server_name(), "example.com");
|
||||
|
Loading…
x
Reference in New Issue
Block a user