Add ServerName identifier
This commit is contained in:
		
							parent
							
								
									8ee1b2302d
								
							
						
					
					
						commit
						f8492db766
					
				| @ -36,9 +36,9 @@ impl DerefMut for DirectEventContent { | |||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use std::collections::BTreeMap; |     use std::{collections::BTreeMap, convert::TryFrom}; | ||||||
| 
 | 
 | ||||||
|     use ruma_identifiers::{RoomId, UserId}; |     use ruma_identifiers::{RoomId, ServerNameRef, UserId}; | ||||||
|     use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; |     use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; | ||||||
| 
 | 
 | ||||||
|     use super::{DirectEvent, DirectEventContent}; |     use super::{DirectEvent, DirectEventContent}; | ||||||
| @ -47,8 +47,9 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn serialization() { |     fn serialization() { | ||||||
|         let mut content = DirectEventContent(BTreeMap::new()); |         let mut content = DirectEventContent(BTreeMap::new()); | ||||||
|         let alice = UserId::new("ruma.io").unwrap(); |         let server_name = ServerNameRef::try_from("ruma.io").unwrap(); | ||||||
|         let room = vec![RoomId::new("ruma.io").unwrap()]; |         let alice = UserId::new(server_name); | ||||||
|  |         let room = vec![RoomId::new(server_name)]; | ||||||
| 
 | 
 | ||||||
|         content.insert(alice.clone(), room.clone()); |         content.insert(alice.clone(), room.clone()); | ||||||
| 
 | 
 | ||||||
| @ -65,8 +66,9 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn deserialization() { |     fn deserialization() { | ||||||
|         let alice = UserId::new("ruma.io").unwrap(); |         let server_name = ServerNameRef::try_from("ruma.io").unwrap(); | ||||||
|         let rooms = vec![RoomId::new("ruma.io").unwrap(), RoomId::new("ruma.io").unwrap()]; |         let alice = UserId::new(server_name); | ||||||
|  |         let rooms = vec![RoomId::new(server_name), RoomId::new(server_name)]; | ||||||
| 
 | 
 | ||||||
|         let json_data = json!({ |         let json_data = json!({ | ||||||
|             "content": { |             "content": { | ||||||
|  | |||||||
| @ -19,9 +19,12 @@ pub struct PinnedEventsEventContent { | |||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use std::time::{Duration, UNIX_EPOCH}; |     use std::{ | ||||||
|  |         convert::TryFrom, | ||||||
|  |         time::{Duration, UNIX_EPOCH}, | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     use ruma_identifiers::{EventId, RoomId, UserId}; |     use ruma_identifiers::{EventId, RoomId, ServerNameRef, UserId}; | ||||||
|     use serde_json::to_string; |     use serde_json::to_string; | ||||||
| 
 | 
 | ||||||
|     use super::PinnedEventsEventContent; |     use super::PinnedEventsEventContent; | ||||||
| @ -30,17 +33,18 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn serialization_deserialization() { |     fn serialization_deserialization() { | ||||||
|         let mut content: PinnedEventsEventContent = PinnedEventsEventContent { pinned: Vec::new() }; |         let mut content: PinnedEventsEventContent = PinnedEventsEventContent { pinned: Vec::new() }; | ||||||
|  |         let server_name = ServerNameRef::try_from("example.com").unwrap(); | ||||||
| 
 | 
 | ||||||
|         content.pinned.push(EventId::new("example.com").unwrap()); |         content.pinned.push(EventId::new(server_name)); | ||||||
|         content.pinned.push(EventId::new("example.com").unwrap()); |         content.pinned.push(EventId::new(server_name)); | ||||||
| 
 | 
 | ||||||
|         let event = StateEvent { |         let event = StateEvent { | ||||||
|             content: content.clone(), |             content: content.clone(), | ||||||
|             event_id: EventId::new("example.com").unwrap(), |             event_id: EventId::new(server_name), | ||||||
|             origin_server_ts: UNIX_EPOCH + Duration::from_millis(1_432_804_485_886u64), |             origin_server_ts: UNIX_EPOCH + Duration::from_millis(1_432_804_485_886u64), | ||||||
|             prev_content: None, |             prev_content: None, | ||||||
|             room_id: RoomId::new("example.com").unwrap(), |             room_id: RoomId::new(server_name), | ||||||
|             sender: UserId::new("example.com").unwrap(), |             sender: UserId::new(server_name), | ||||||
|             state_key: "".to_string(), |             state_key: "".to_string(), | ||||||
|             unsigned: UnsignedData::default(), |             unsigned: UnsignedData::default(), | ||||||
|         }; |         }; | ||||||
|  | |||||||
| @ -8,10 +8,35 @@ Breaking changes: | |||||||
| * Update `parse_with_server_name`s signature (instead of `Into<String>` it now requires | * Update `parse_with_server_name`s signature (instead of `Into<String>` it now requires | ||||||
|   `Into<Box<str>>` of the id type). This is technically a breaking change, but extremely unlikely |   `Into<Box<str>>` of the id type). This is technically a breaking change, but extremely unlikely | ||||||
|   to affect any existing code. |   to affect any existing code. | ||||||
|  | * Modify identifier types to use the new `ServerName` type: | ||||||
|  |   * Change signature of `new()` methods of `EventId`, `RoomId`, and `UserId` from | ||||||
|  |     ```rust | ||||||
|  |     fn new(&str) -> Result<Self, Error> | ||||||
|  |     //... | ||||||
|  |     ``` | ||||||
|  |     to | ||||||
|  |     ```rust | ||||||
|  |     fn new(ServerNameRef<'_>) -> Self | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  |   * Change signature of `server_name()` for `EventId`, `RoomAliasId`, `RoomId`, `RoomIdOrAliasId`, `UserId` from | ||||||
|  |     ```rust | ||||||
|  |     fn server_name() -> &str | ||||||
|  |     ``` | ||||||
|  |     to | ||||||
|  |     ```rust | ||||||
|  |     fn server_name() -> ServerNameRef<'_> | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | Deprecations: | ||||||
|  | 
 | ||||||
|  | * Mark `server_name::is_valid_server_name` as deprecated in favor of `ServerName::try_from()` | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| Improvements: | Improvements: | ||||||
| 
 | 
 | ||||||
| * Add `DeviceKeyId`, `DeviceKeyAlgorithm`, `ServerKeyId`, and `ServerKeyAlgorithm` | * Add `DeviceKeyId`, `DeviceKeyAlgorithm`, `ServerKeyId`, and `ServerKeyAlgorithm` | ||||||
|  | * Add `ServerName` and `ServerNameRef` types | ||||||
| 
 | 
 | ||||||
| # 0.16.2 | # 0.16.2 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| //! Matrix event identifiers.
 | //! Matrix event identifiers.
 | ||||||
| 
 | 
 | ||||||
| use std::num::NonZeroU8; | use std::{convert::TryFrom, num::NonZeroU8}; | ||||||
| 
 | 
 | ||||||
| use crate::{error::Error, parse_id, validate_id}; | use crate::{error::Error, parse_id, validate_id, ServerNameRef}; | ||||||
| 
 | 
 | ||||||
| /// A Matrix event ID.
 | /// A Matrix event ID.
 | ||||||
| ///
 | ///
 | ||||||
| @ -55,18 +55,15 @@ impl<T> EventId<T> { | |||||||
|     /// parsed as a valid host.
 |     /// parsed as a valid host.
 | ||||||
|     #[cfg(feature = "rand")] |     #[cfg(feature = "rand")] | ||||||
|     #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] |     #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] | ||||||
|     pub fn new(server_name: &str) -> Result<Self, Error> |     pub fn new(server_name: ServerNameRef<'_>) -> Self | ||||||
|     where |     where | ||||||
|         String: Into<T>, |         String: Into<T>, | ||||||
|     { |     { | ||||||
|         use crate::{generate_localpart, is_valid_server_name}; |         use crate::generate_localpart; | ||||||
| 
 | 
 | ||||||
|         if !is_valid_server_name(server_name) { |  | ||||||
|             return Err(Error::InvalidServerName); |  | ||||||
|         } |  | ||||||
|         let full_id = format!("${}:{}", generate_localpart(18), server_name).into(); |         let full_id = format!("${}:{}", generate_localpart(18), server_name).into(); | ||||||
| 
 | 
 | ||||||
|         Ok(Self { full_id, colon_idx: NonZeroU8::new(19) }) |         Self { full_id, colon_idx: NonZeroU8::new(19) } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the event's unique ID. For the original event format as used by Matrix room
 |     /// Returns the event's unique ID. For the original event format as used by Matrix room
 | ||||||
| @ -87,11 +84,13 @@ impl<T> EventId<T> { | |||||||
|     /// Returns the server name of the event ID.
 |     /// 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.
 |     /// 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<ServerNameRef<'_>> | ||||||
|     where |     where | ||||||
|         T: AsRef<str>, |         T: AsRef<str>, | ||||||
|     { |     { | ||||||
|         self.colon_idx.map(|idx| &self.full_id.as_ref()[idx.get() as usize + 1..]) |         self.colon_idx.map(|idx| { | ||||||
|  |             ServerNameRef::try_from(&self.full_id.as_ref()[idx.get() as usize + 1..]).unwrap() | ||||||
|  |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -123,7 +122,7 @@ mod tests { | |||||||
|     #[cfg(feature = "serde")] |     #[cfg(feature = "serde")] | ||||||
|     use serde_json::{from_str, to_string}; |     use serde_json::{from_str, to_string}; | ||||||
| 
 | 
 | ||||||
|     use crate::error::Error; |     use crate::{error::Error, ServerNameRef}; | ||||||
| 
 | 
 | ||||||
|     type EventId = super::EventId<Box<str>>; |     type EventId = super::EventId<Box<str>>; | ||||||
| 
 | 
 | ||||||
| @ -160,19 +159,15 @@ mod tests { | |||||||
|     #[cfg(feature = "rand")] |     #[cfg(feature = "rand")] | ||||||
|     #[test] |     #[test] | ||||||
|     fn generate_random_valid_event_id() { |     fn generate_random_valid_event_id() { | ||||||
|         let event_id = EventId::new("example.com").expect("Failed to generate EventId."); |         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: &str = event_id.as_ref(); | ||||||
| 
 | 
 | ||||||
|         assert!(id_str.starts_with('$')); |         assert!(id_str.starts_with('$')); | ||||||
|         assert_eq!(id_str.len(), 31); |         assert_eq!(id_str.len(), 31); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[cfg(feature = "rand")] |  | ||||||
|     #[test] |  | ||||||
|     fn generate_random_invalid_event_id() { |  | ||||||
|         assert!(EventId::new("").is_err()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[cfg(feature = "serde")] |     #[cfg(feature = "serde")] | ||||||
|     #[test] |     #[test] | ||||||
|     fn serialize_valid_original_event_id() { |     fn serialize_valid_original_event_id() { | ||||||
|  | |||||||
| @ -11,19 +11,19 @@ | |||||||
| #![allow(clippy::use_self)] | #![allow(clippy::use_self)] | ||||||
| #![cfg_attr(docsrs, feature(doc_cfg))] | #![cfg_attr(docsrs, feature(doc_cfg))] | ||||||
| 
 | 
 | ||||||
| use std::num::NonZeroU8; | use std::{convert::TryFrom, num::NonZeroU8}; | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "serde")] | #[cfg(feature = "serde")] | ||||||
| use serde::de::{self, Deserialize as _, Deserializer, Unexpected}; | use serde::de::{self, Deserialize as _, Deserializer, Unexpected}; | ||||||
| 
 | 
 | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
|  | #[allow(deprecated)] | ||||||
| pub use crate::{error::Error, server_name::is_valid_server_name}; | pub use crate::{error::Error, server_name::is_valid_server_name}; | ||||||
| 
 | 
 | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod macros; | mod macros; | ||||||
| 
 | 
 | ||||||
| mod error; | mod error; | ||||||
| mod server_name; |  | ||||||
| 
 | 
 | ||||||
| pub mod device_id; | pub mod device_id; | ||||||
| pub mod device_key_id; | pub mod device_key_id; | ||||||
| @ -34,6 +34,8 @@ pub mod room_id; | |||||||
| pub mod room_id_or_room_alias_id; | pub mod room_id_or_room_alias_id; | ||||||
| pub mod room_version_id; | pub mod room_version_id; | ||||||
| pub mod server_key_id; | pub mod server_key_id; | ||||||
|  | #[allow(deprecated)] | ||||||
|  | pub mod server_name; | ||||||
| pub mod user_id; | pub mod user_id; | ||||||
| 
 | 
 | ||||||
| /// Allowed algorithms for homeserver signing keys.
 | /// Allowed algorithms for homeserver signing keys.
 | ||||||
| @ -127,13 +129,24 @@ pub type ServerKeyAlgorithm = key_algorithms::ServerKeyAlgorithm; | |||||||
| /// and `Deserialize` if the `serde` feature is enabled.
 | /// and `Deserialize` if the `serde` feature is enabled.
 | ||||||
| pub type ServerKeyId = server_key_id::ServerKeyId<Box<str>>; | pub type ServerKeyId = server_key_id::ServerKeyId<Box<str>>; | ||||||
| 
 | 
 | ||||||
| /// An reference to a homeserver signing key identifier containing a key
 | /// A reference to a homeserver signing key identifier containing a key
 | ||||||
| /// algorithm and version.
 | /// algorithm and version.
 | ||||||
| ///
 | ///
 | ||||||
| /// Can be created via `TryFrom<&str>`; implements `Serialize`
 | /// Can be created via `TryFrom<&str>`; implements `Serialize`
 | ||||||
| /// and `Deserialize` if the `serde` feature is enabled.
 | /// and `Deserialize` if the `serde` feature is enabled.
 | ||||||
| pub type ServerKeyIdRef<'a> = server_key_id::ServerKeyId<&'a str>; | 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.
 | /// An owned user ID.
 | ||||||
| ///
 | ///
 | ||||||
| /// Can be created via `new` (if the `rand` feature is enabled) and `TryFrom<String>` +
 | /// Can be created via `new` (if the `rand` feature is enabled) and `TryFrom<String>` +
 | ||||||
| @ -187,9 +200,7 @@ fn parse_id(id: &str, valid_sigils: &[char]) -> Result<NonZeroU8, Error> { | |||||||
|         return Err(Error::InvalidLocalPart); |         return Err(Error::InvalidLocalPart); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if !is_valid_server_name(&id[colon_idx + 1..]) { |     server_name::ServerName::<&str>::try_from(&id[colon_idx + 1..])?; | ||||||
|         return Err(Error::InvalidServerName); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     Ok(NonZeroU8::new(colon_idx as u8).unwrap()) |     Ok(NonZeroU8::new(colon_idx as u8).unwrap()) | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| //! Matrix room alias identifiers.
 | //! Matrix room alias identifiers.
 | ||||||
| 
 | 
 | ||||||
| use std::num::NonZeroU8; | use std::{convert::TryFrom, num::NonZeroU8}; | ||||||
| 
 | 
 | ||||||
| use crate::{error::Error, parse_id}; | use crate::{error::Error, parse_id, server_name::ServerName}; | ||||||
| 
 | 
 | ||||||
| /// A Matrix room alias ID.
 | /// A Matrix room alias ID.
 | ||||||
| ///
 | ///
 | ||||||
| @ -33,8 +33,8 @@ impl<T: AsRef<str>> RoomAliasId<T> { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the server name of the room alias ID.
 |     /// Returns the server name of the room alias ID.
 | ||||||
|     pub fn server_name(&self) -> &str { |     pub fn server_name(&self) -> ServerName<&str> { | ||||||
|         &self.full_id.as_ref()[self.colon_idx.get() as usize + 1..] |         ServerName::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]).unwrap() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| //! Matrix room identifiers.
 | //! Matrix room identifiers.
 | ||||||
| 
 | 
 | ||||||
| use std::num::NonZeroU8; | use std::{convert::TryFrom, num::NonZeroU8}; | ||||||
| 
 | 
 | ||||||
| use crate::{error::Error, parse_id}; | use crate::{error::Error, parse_id, ServerNameRef}; | ||||||
| 
 | 
 | ||||||
| /// A Matrix room ID.
 | /// A Matrix room ID.
 | ||||||
| ///
 | ///
 | ||||||
| @ -33,18 +33,15 @@ impl<T> RoomId<T> { | |||||||
|     /// Fails if the given homeserver cannot be parsed as a valid host.
 |     /// Fails if the given homeserver cannot be parsed as a valid host.
 | ||||||
|     #[cfg(feature = "rand")] |     #[cfg(feature = "rand")] | ||||||
|     #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] |     #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] | ||||||
|     pub fn new(server_name: &str) -> Result<Self, Error> |     pub fn new(server_name: ServerNameRef<'_>) -> Self | ||||||
|     where |     where | ||||||
|         String: Into<T>, |         String: Into<T>, | ||||||
|     { |     { | ||||||
|         use crate::{generate_localpart, is_valid_server_name}; |         use crate::generate_localpart; | ||||||
| 
 | 
 | ||||||
|         if !is_valid_server_name(server_name) { |  | ||||||
|             return Err(Error::InvalidServerName); |  | ||||||
|         } |  | ||||||
|         let full_id = format!("!{}:{}", generate_localpart(18), server_name).into(); |         let full_id = format!("!{}:{}", generate_localpart(18), server_name).into(); | ||||||
| 
 | 
 | ||||||
|         Ok(Self { full_id, colon_idx: NonZeroU8::new(19).unwrap() }) |         Self { full_id, colon_idx: NonZeroU8::new(19).unwrap() } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the rooms's unique ID.
 |     /// Returns the rooms's unique ID.
 | ||||||
| @ -56,11 +53,12 @@ impl<T> RoomId<T> { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the server name of the room ID.
 |     /// Returns the server name of the room ID.
 | ||||||
|     pub fn server_name(&self) -> &str |     pub fn server_name(&self) -> ServerNameRef<'_> | ||||||
|     where |     where | ||||||
|         T: AsRef<str>, |         T: AsRef<str>, | ||||||
|     { |     { | ||||||
|         &self.full_id.as_ref()[self.colon_idx.get() as usize + 1..] |         ServerNameRef::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]) | ||||||
|  |             .unwrap() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -85,7 +83,7 @@ mod tests { | |||||||
|     #[cfg(feature = "serde")] |     #[cfg(feature = "serde")] | ||||||
|     use serde_json::{from_str, to_string}; |     use serde_json::{from_str, to_string}; | ||||||
| 
 | 
 | ||||||
|     use crate::error::Error; |     use crate::{error::Error, ServerNameRef}; | ||||||
| 
 | 
 | ||||||
|     type RoomId = super::RoomId<Box<str>>; |     type RoomId = super::RoomId<Box<str>>; | ||||||
| 
 | 
 | ||||||
| @ -102,19 +100,15 @@ mod tests { | |||||||
|     #[cfg(feature = "rand")] |     #[cfg(feature = "rand")] | ||||||
|     #[test] |     #[test] | ||||||
|     fn generate_random_valid_room_id() { |     fn generate_random_valid_room_id() { | ||||||
|         let room_id = RoomId::new("example.com").expect("Failed to generate RoomId."); |         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: &str = room_id.as_ref(); | ||||||
| 
 | 
 | ||||||
|         assert!(id_str.starts_with('!')); |         assert!(id_str.starts_with('!')); | ||||||
|         assert_eq!(id_str.len(), 31); |         assert_eq!(id_str.len(), 31); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[cfg(feature = "rand")] |  | ||||||
|     #[test] |  | ||||||
|     fn generate_random_invalid_room_id() { |  | ||||||
|         assert!(RoomId::new("").is_err()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[cfg(feature = "serde")] |     #[cfg(feature = "serde")] | ||||||
|     #[test] |     #[test] | ||||||
|     fn serialize_valid_room_id() { |     fn serialize_valid_room_id() { | ||||||
|  | |||||||
| @ -2,7 +2,9 @@ | |||||||
| 
 | 
 | ||||||
| use std::{convert::TryFrom, hint::unreachable_unchecked, num::NonZeroU8}; | use std::{convert::TryFrom, hint::unreachable_unchecked, num::NonZeroU8}; | ||||||
| 
 | 
 | ||||||
| use crate::{error::Error, parse_id, room_alias_id::RoomAliasId, room_id::RoomId}; | use crate::{ | ||||||
|  |     error::Error, parse_id, room_alias_id::RoomAliasId, room_id::RoomId, server_name::ServerName, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| /// A Matrix room ID or a Matrix room alias ID.
 | /// A Matrix room ID or a Matrix room alias ID.
 | ||||||
| ///
 | ///
 | ||||||
| @ -39,8 +41,8 @@ impl<T: AsRef<str>> RoomIdOrAliasId<T> { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the server name of the room (alias) ID.
 |     /// Returns the server name of the room (alias) ID.
 | ||||||
|     pub fn server_name(&self) -> &str { |     pub fn server_name(&self) -> ServerName<&str> { | ||||||
|         &self.full_id.as_ref()[self.colon_idx.get() as usize + 1..] |         ServerName::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]).unwrap() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Whether this is a room id (starts with `'!'`)
 |     /// Whether this is a room id (starts with `'!'`)
 | ||||||
|  | |||||||
| @ -1,6 +1,35 @@ | |||||||
|  | //! Matrix-spec compliant server names.
 | ||||||
|  | 
 | ||||||
|  | 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, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn try_from<S, T>(server_name: S) -> Result<ServerName<T>, Error> | ||||||
|  | where | ||||||
|  |     S: AsRef<str> + Into<T>, | ||||||
|  | { | ||||||
|  |     if !is_valid_server_name(server_name.as_ref()) { | ||||||
|  |         return Err(Error::InvalidServerName); | ||||||
|  |     } | ||||||
|  |     Ok(ServerName { full_id: server_name.into() }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | common_impls!(ServerName, try_from, "An IP address or hostname"); | ||||||
|  | 
 | ||||||
| /// Check whether a given string is a valid server name according to [the specification][].
 | /// Check whether a given string is a valid server name according to [the specification][].
 | ||||||
| ///
 | ///
 | ||||||
|  | /// Deprecated. Use the `try_from()` method of [`ServerName`](server_name/struct.ServerName.html) to construct
 | ||||||
|  | /// a server name instead.
 | ||||||
|  | ///
 | ||||||
| /// [the specification]: https://matrix.org/docs/spec/appendices#server-name
 | /// [the specification]: https://matrix.org/docs/spec/appendices#server-name
 | ||||||
|  | #[deprecated] | ||||||
| pub fn is_valid_server_name(name: &str) -> bool { | pub fn is_valid_server_name(name: &str) -> bool { | ||||||
|     use std::net::Ipv6Addr; |     use std::net::Ipv6Addr; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| //! Matrix user identifiers.
 | //! Matrix user identifiers.
 | ||||||
| 
 | 
 | ||||||
| use std::num::NonZeroU8; | use std::{convert::TryFrom, num::NonZeroU8}; | ||||||
| 
 | 
 | ||||||
| use crate::{error::Error, is_valid_server_name, parse_id}; | use crate::{error::Error, parse_id, ServerNameRef}; | ||||||
| 
 | 
 | ||||||
| /// A Matrix user ID.
 | /// A Matrix user ID.
 | ||||||
| ///
 | ///
 | ||||||
| @ -35,22 +35,17 @@ pub struct UserId<T> { | |||||||
| impl<T> UserId<T> { | impl<T> UserId<T> { | ||||||
|     /// Attempts to generate a `UserId` for the given origin server with a localpart consisting of
 |     /// Attempts to generate a `UserId` for the given origin server with a localpart consisting of
 | ||||||
|     /// 12 random ASCII characters.
 |     /// 12 random ASCII characters.
 | ||||||
|     ///
 |  | ||||||
|     /// Fails if the given homeserver cannot be parsed as a valid host.
 |  | ||||||
|     #[cfg(feature = "rand")] |     #[cfg(feature = "rand")] | ||||||
|     #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] |     #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] | ||||||
|     pub fn new(server_name: &str) -> Result<Self, Error> |     pub fn new(server_name: ServerNameRef<'_>) -> Self | ||||||
|     where |     where | ||||||
|         String: Into<T>, |         String: Into<T>, | ||||||
|     { |     { | ||||||
|         use crate::generate_localpart; |         use crate::generate_localpart; | ||||||
| 
 | 
 | ||||||
|         if !is_valid_server_name(server_name) { |  | ||||||
|             return Err(Error::InvalidServerName); |  | ||||||
|         } |  | ||||||
|         let full_id = format!("@{}:{}", generate_localpart(12).to_lowercase(), server_name).into(); |         let full_id = format!("@{}:{}", generate_localpart(12).to_lowercase(), server_name).into(); | ||||||
| 
 | 
 | ||||||
|         Ok(Self { full_id, colon_idx: NonZeroU8::new(13).unwrap(), is_historical: false }) |         Self { full_id, colon_idx: NonZeroU8::new(13).unwrap(), is_historical: false } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Attempts to complete a user ID, by adding the colon + server name and `@` prefix, if not
 |     /// Attempts to complete a user ID, by adding the colon + server name and `@` prefix, if not
 | ||||||
| @ -62,7 +57,7 @@ impl<T> UserId<T> { | |||||||
|     /// the `@` prefix.
 |     /// the `@` prefix.
 | ||||||
|     pub fn parse_with_server_name( |     pub fn parse_with_server_name( | ||||||
|         id: impl AsRef<str> + Into<T>, |         id: impl AsRef<str> + Into<T>, | ||||||
|         server_name: &str, |         server_name: ServerNameRef<'_>, | ||||||
|     ) -> Result<Self, Error> |     ) -> Result<Self, Error> | ||||||
|     where |     where | ||||||
|         String: Into<T>, |         String: Into<T>, | ||||||
| @ -73,9 +68,6 @@ impl<T> UserId<T> { | |||||||
|             try_from(id) |             try_from(id) | ||||||
|         } else { |         } else { | ||||||
|             let is_fully_conforming = localpart_is_fully_comforming(id_str)?; |             let is_fully_conforming = localpart_is_fully_comforming(id_str)?; | ||||||
|             if !is_valid_server_name(server_name) { |  | ||||||
|                 return Err(Error::InvalidServerName); |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             Ok(Self { |             Ok(Self { | ||||||
|                 full_id: format!("@{}:{}", id_str, server_name).into(), |                 full_id: format!("@{}:{}", id_str, server_name).into(), | ||||||
| @ -94,11 +86,12 @@ impl<T> UserId<T> { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the server name of the user ID.
 |     /// Returns the server name of the user ID.
 | ||||||
|     pub fn server_name(&self) -> &str |     pub fn server_name(&self) -> ServerNameRef<'_> | ||||||
|     where |     where | ||||||
|         T: AsRef<str>, |         T: AsRef<str>, | ||||||
|     { |     { | ||||||
|         &self.full_id.as_ref()[self.colon_idx.get() as usize + 1..] |         ServerNameRef::try_from(&self.full_id.as_ref()[self.colon_idx.get() as usize + 1..]) | ||||||
|  |             .unwrap() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Whether this user ID is a historical one, i.e. one that doesn't conform to the latest
 |     /// Whether this user ID is a historical one, i.e. one that doesn't conform to the latest
 | ||||||
| @ -158,7 +151,7 @@ mod tests { | |||||||
|     #[cfg(feature = "serde")] |     #[cfg(feature = "serde")] | ||||||
|     use serde_json::{from_str, to_string}; |     use serde_json::{from_str, to_string}; | ||||||
| 
 | 
 | ||||||
|     use crate::error::Error; |     use crate::{error::Error, ServerNameRef}; | ||||||
| 
 | 
 | ||||||
|     type UserId = super::UserId<Box<str>>; |     type UserId = super::UserId<Box<str>>; | ||||||
| 
 | 
 | ||||||
| @ -173,7 +166,8 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn parse_valid_user_id() { |     fn parse_valid_user_id() { | ||||||
|         let user_id = UserId::parse_with_server_name("@carl:example.com", "example.com") |         let server_name = ServerNameRef::try_from("example.com").unwrap(); | ||||||
|  |         let user_id = UserId::parse_with_server_name("@carl:example.com", server_name) | ||||||
|             .expect("Failed to create UserId."); |             .expect("Failed to create UserId."); | ||||||
|         assert_eq!(user_id.as_ref(), "@carl:example.com"); |         assert_eq!(user_id.as_ref(), "@carl:example.com"); | ||||||
|         assert_eq!(user_id.localpart(), "carl"); |         assert_eq!(user_id.localpart(), "carl"); | ||||||
| @ -183,8 +177,9 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn parse_valid_user_id_parts() { |     fn parse_valid_user_id_parts() { | ||||||
|         let user_id = UserId::parse_with_server_name("carl", "example.com") |         let server_name = ServerNameRef::try_from("example.com").unwrap(); | ||||||
|             .expect("Failed to create UserId."); |         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"); |         assert_eq!(user_id.as_ref(), "@carl:example.com"); | ||||||
|         assert_eq!(user_id.localpart(), "carl"); |         assert_eq!(user_id.localpart(), "carl"); | ||||||
|         assert_eq!(user_id.server_name(), "example.com"); |         assert_eq!(user_id.server_name(), "example.com"); | ||||||
| @ -202,7 +197,8 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn parse_valid_historical_user_id() { |     fn parse_valid_historical_user_id() { | ||||||
|         let user_id = UserId::parse_with_server_name("@a%b[irc]:example.com", "example.com") |         let server_name = ServerNameRef::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."); |             .expect("Failed to create UserId."); | ||||||
|         assert_eq!(user_id.as_ref(), "@a%b[irc]:example.com"); |         assert_eq!(user_id.as_ref(), "@a%b[irc]:example.com"); | ||||||
|         assert_eq!(user_id.localpart(), "a%b[irc]"); |         assert_eq!(user_id.localpart(), "a%b[irc]"); | ||||||
| @ -212,7 +208,8 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn parse_valid_historical_user_id_parts() { |     fn parse_valid_historical_user_id_parts() { | ||||||
|         let user_id = UserId::parse_with_server_name("a%b[irc]", "example.com") |         let server_name = ServerNameRef::try_from("example.com").unwrap(); | ||||||
|  |         let user_id = UserId::parse_with_server_name("a%b[irc]", server_name) | ||||||
|             .expect("Failed to create UserId."); |             .expect("Failed to create UserId."); | ||||||
|         assert_eq!(user_id.as_ref(), "@a%b[irc]:example.com"); |         assert_eq!(user_id.as_ref(), "@a%b[irc]:example.com"); | ||||||
|         assert_eq!(user_id.localpart(), "a%b[irc]"); |         assert_eq!(user_id.localpart(), "a%b[irc]"); | ||||||
| @ -230,7 +227,8 @@ mod tests { | |||||||
|     #[cfg(feature = "rand")] |     #[cfg(feature = "rand")] | ||||||
|     #[test] |     #[test] | ||||||
|     fn generate_random_valid_user_id() { |     fn generate_random_valid_user_id() { | ||||||
|         let user_id = UserId::new("example.com").expect("Failed to generate UserId."); |         let server_name = ServerNameRef::try_from("example.com").unwrap(); | ||||||
|  |         let user_id = UserId::new(server_name); | ||||||
|         assert_eq!(user_id.localpart().len(), 12); |         assert_eq!(user_id.localpart().len(), 12); | ||||||
|         assert_eq!(user_id.server_name(), "example.com"); |         assert_eq!(user_id.server_name(), "example.com"); | ||||||
| 
 | 
 | ||||||
| @ -240,12 +238,6 @@ mod tests { | |||||||
|         assert_eq!(id_str.len(), 25); |         assert_eq!(id_str.len(), 25); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[cfg(feature = "rand")] |  | ||||||
|     #[test] |  | ||||||
|     fn generate_random_invalid_user_id() { |  | ||||||
|         assert!(UserId::new("").is_err()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[cfg(feature = "serde")] |     #[cfg(feature = "serde")] | ||||||
|     #[test] |     #[test] | ||||||
|     fn serialize_valid_user_id() { |     fn serialize_valid_user_id() { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user