identifiers: Replace most macro_rules! code with proc-macro code
This commit is contained in:
parent
a2df988c23
commit
f658487c50
@ -33,9 +33,6 @@ pub use self::{
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use ruma_identifiers_validation::error::Error as IdParseError;
|
pub use ruma_identifiers_validation::error::Error as IdParseError;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
|
|
||||||
pub mod matrix_uri;
|
pub mod matrix_uri;
|
||||||
pub mod user_id;
|
pub mod user_id;
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! Client secret identifier.
|
//! Client secret identifier.
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
/// A client secret.
|
/// A client secret.
|
||||||
///
|
///
|
||||||
/// Client secrets in Matrix are opaque character sequences of `[0-9a-zA-Z.=_-]`. Their length must
|
/// Client secrets in Matrix are opaque character sequences of `[0-9a-zA-Z.=_-]`. Their length must
|
||||||
@ -9,7 +11,8 @@
|
|||||||
/// use `ClientSecret::new()` to generate a random one. If that function is not available for you,
|
/// use `ClientSecret::new()` to generate a random one. If that function is not available for you,
|
||||||
/// you need to activate this crate's `rand` Cargo feature.
|
/// you need to activate this crate's `rand` Cargo feature.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::client_secret::validate)]
|
||||||
pub struct ClientSecret(str);
|
pub struct ClientSecret(str);
|
||||||
|
|
||||||
impl ClientSecret {
|
impl ClientSecret {
|
||||||
@ -24,14 +27,6 @@ impl ClientSecret {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
owned_identifier!(OwnedClientSecret, ClientSecret);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(
|
|
||||||
ClientSecret,
|
|
||||||
OwnedClientSecret,
|
|
||||||
ruma_identifiers_validation::client_secret::validate
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
#[cfg(feature = "rand")]
|
||||||
use super::generate_localpart;
|
use super::generate_localpart;
|
||||||
|
|
||||||
@ -26,13 +28,9 @@ use super::generate_localpart;
|
|||||||
/// assert_eq!(owned_id.as_str(), "ijklmnop");
|
/// assert_eq!(owned_id.as_str(), "ijklmnop");
|
||||||
/// ```
|
/// ```
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
pub struct DeviceId(str);
|
pub struct DeviceId(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedDeviceId, DeviceId);
|
|
||||||
|
|
||||||
opaque_identifier!(DeviceId, OwnedDeviceId);
|
|
||||||
|
|
||||||
impl DeviceId {
|
impl DeviceId {
|
||||||
/// Generates a random `DeviceId`, suitable for assignment to a new device.
|
/// Generates a random `DeviceId`, suitable for assignment to a new device.
|
||||||
#[cfg(feature = "rand")]
|
#[cfg(feature = "rand")]
|
||||||
|
@ -1,20 +1,15 @@
|
|||||||
//! Identifiers for device keys for end-to-end encryption.
|
//! Identifiers for device keys for end-to-end encryption.
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
use super::{crypto_algorithms::DeviceKeyAlgorithm, DeviceId};
|
use super::{crypto_algorithms::DeviceKeyAlgorithm, DeviceId};
|
||||||
|
|
||||||
/// A key algorithm and a device id, combined with a ':'.
|
/// A key algorithm and a device id, combined with a ':'.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::device_key_id::validate)]
|
||||||
pub struct DeviceKeyId(str);
|
pub struct DeviceKeyId(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedDeviceKeyId, DeviceKeyId);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(
|
|
||||||
DeviceKeyId,
|
|
||||||
OwnedDeviceKeyId,
|
|
||||||
ruma_identifiers_validation::device_key_id::validate
|
|
||||||
);
|
|
||||||
|
|
||||||
impl DeviceKeyId {
|
impl DeviceKeyId {
|
||||||
/// Create a `DeviceKeyId` from a `DeviceKeyAlgorithm` and a `DeviceId`.
|
/// Create a `DeviceKeyId` from a `DeviceKeyAlgorithm` and a `DeviceId`.
|
||||||
pub fn from_parts(algorithm: DeviceKeyAlgorithm, device_id: &DeviceId) -> Box<Self> {
|
pub fn from_parts(algorithm: DeviceKeyAlgorithm, device_id: &DeviceId) -> Box<Self> {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! Matrix event identifiers.
|
//! Matrix event identifiers.
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
use super::ServerName;
|
use super::ServerName;
|
||||||
|
|
||||||
/// A Matrix [event ID].
|
/// A Matrix [event ID].
|
||||||
@ -35,17 +37,10 @@ use super::ServerName;
|
|||||||
///
|
///
|
||||||
/// [event ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
|
/// [event ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::event_id::validate)]
|
||||||
pub struct EventId(str);
|
pub struct EventId(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedEventId, EventId);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(
|
|
||||||
EventId,
|
|
||||||
OwnedEventId,
|
|
||||||
ruma_identifiers_validation::event_id::validate
|
|
||||||
);
|
|
||||||
|
|
||||||
impl EventId {
|
impl EventId {
|
||||||
/// Attempts to generate an `EventId` for the given origin server with a localpart consisting
|
/// Attempts to generate an `EventId` for the given origin server with a localpart consisting
|
||||||
/// of 18 random ASCII characters.
|
/// of 18 random ASCII characters.
|
||||||
|
@ -263,5 +263,24 @@ impl<A, K: ?Sized> TryFrom<String> for Box<KeyId<A, K>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! partial_eq_string {
|
||||||
|
($id:ty $([$( $g:ident ),*])?) => {
|
||||||
|
partial_eq_string!(@imp $(<$($g),*>)?, $id, str);
|
||||||
|
partial_eq_string!(@imp $(<$($g),*>)?, $id, &str);
|
||||||
|
partial_eq_string!(@imp $(<$($g),*>)?, $id, String);
|
||||||
|
partial_eq_string!(@imp $(<$($g),*>)?, str, $id);
|
||||||
|
partial_eq_string!(@imp $(<$($g),*>)?, &str, $id);
|
||||||
|
partial_eq_string!(@imp $(<$($g),*>)?, String, $id);
|
||||||
|
};
|
||||||
|
(@imp $(<$( $g:ident ),*>)?, $l:ty, $r:ty) => {
|
||||||
|
impl $(<$($g),*>)? PartialEq<$r> for $l {
|
||||||
|
fn eq(&self, other: &$r) -> bool {
|
||||||
|
AsRef::<str>::as_ref(self)
|
||||||
|
== AsRef::<str>::as_ref(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
partial_eq_string!(KeyId<A, K> [A, K]);
|
partial_eq_string!(KeyId<A, K> [A, K]);
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
/// A Matrix key identifier.
|
/// A Matrix key identifier.
|
||||||
///
|
///
|
||||||
/// Key identifiers in Matrix are opaque character sequences of `[a-zA-Z_]`. This type is
|
/// Key identifiers in Matrix are opaque character sequences of `[a-zA-Z_]`. This type is
|
||||||
/// provided simply for its semantic value.
|
/// provided simply for its semantic value.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
pub struct KeyName(str);
|
pub struct KeyName(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedKeyName, KeyName);
|
|
||||||
|
|
||||||
opaque_identifier!(KeyName, OwnedKeyName);
|
|
||||||
|
@ -1,438 +0,0 @@
|
|||||||
/// Declares an item with a doc attribute computed by some macro expression.
|
|
||||||
/// This allows documentation to be dynamically generated based on input.
|
|
||||||
/// Necessary to work around <https://github.com/rust-lang/rust/issues/52607>.
|
|
||||||
macro_rules! doc_concat {
|
|
||||||
( $( #[doc = $doc:expr] $( $thing:tt )* )* ) => ( $( #[doc = $doc] $( $thing )* )* );
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! partial_eq_string {
|
|
||||||
($id:ty $([$( $g:ident ),*])?) => {
|
|
||||||
partial_eq_string!(@imp $(<$($g),*>)?, $id, str);
|
|
||||||
partial_eq_string!(@imp $(<$($g),*>)?, $id, &str);
|
|
||||||
partial_eq_string!(@imp $(<$($g),*>)?, $id, String);
|
|
||||||
partial_eq_string!(@imp $(<$($g),*>)?, str, $id);
|
|
||||||
partial_eq_string!(@imp $(<$($g),*>)?, &str, $id);
|
|
||||||
partial_eq_string!(@imp $(<$($g),*>)?, String, $id);
|
|
||||||
};
|
|
||||||
(@imp $(<$( $g:ident ),*>)?, $l:ty, $r:ty) => {
|
|
||||||
impl $(<$($g),*>)? PartialEq<$r> for $l {
|
|
||||||
fn eq(&self, other: &$r) -> bool {
|
|
||||||
AsRef::<str>::as_ref(self)
|
|
||||||
== AsRef::<str>::as_ref(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! owned_identifier {
|
|
||||||
($owned:ident, $id:ident) => {
|
|
||||||
#[doc = concat!("Owned variant of ", stringify!($id))]
|
|
||||||
///
|
|
||||||
/// The wrapper type for this type is variable, by default it'll use [`Box`],
|
|
||||||
/// but you can change that by setting "`--cfg=ruma_identifiers_storage=...`" using
|
|
||||||
/// `RUSTFLAGS` or `.cargo/config.toml` (under `[build]` -> `rustflags = ["..."]`)
|
|
||||||
/// to the following;
|
|
||||||
/// - `ruma_identifiers_storage="Arc"` to use [`Arc`](std::sync::Arc) as a wrapper type.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct $owned {
|
|
||||||
#[cfg(not(any(ruma_identifiers_storage = "Arc")))]
|
|
||||||
inner: Box<$id>,
|
|
||||||
#[cfg(ruma_identifiers_storage = "Arc")]
|
|
||||||
inner: std::sync::Arc<$id>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<$id> for $owned {
|
|
||||||
fn as_ref(&self) -> &$id {
|
|
||||||
&*self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for $owned {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
(*self.inner).as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for $owned {
|
|
||||||
type Target = $id;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::borrow::Borrow<$id> for $owned {
|
|
||||||
fn borrow(&self) -> &$id {
|
|
||||||
self.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&'_ $id> for $owned {
|
|
||||||
fn from(id: &$id) -> $owned {
|
|
||||||
$owned { inner: id.into() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<$id>> for $owned {
|
|
||||||
fn from(b: Box<$id>) -> $owned {
|
|
||||||
Self { inner: b.into() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::sync::Arc<$id>> for $owned {
|
|
||||||
fn from(a: std::sync::Arc<$id>) -> $owned {
|
|
||||||
Self {
|
|
||||||
#[cfg(not(any(ruma_identifiers_storage = "Arc")))]
|
|
||||||
inner: a.as_ref().into(),
|
|
||||||
#[cfg(ruma_identifiers_storage = "Arc")]
|
|
||||||
inner: a,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl serde::Serialize for $owned {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
serializer.serialize_str(self.as_ref().as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
partial_eq_string!($owned);
|
|
||||||
|
|
||||||
impl PartialEq<Box<$id>> for $owned {
|
|
||||||
fn eq(&self, other: &Box<$id>) -> bool {
|
|
||||||
AsRef::<$id>::as_ref(self) == AsRef::<$id>::as_ref(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<$owned> for Box<$id> {
|
|
||||||
fn eq(&self, other: &$owned) -> bool {
|
|
||||||
AsRef::<$id>::as_ref(self) == AsRef::<$id>::as_ref(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! opaque_identifier_common_impls {
|
|
||||||
($id:ident, $owned:ident) => {
|
|
||||||
impl $id {
|
|
||||||
pub(super) fn from_borrowed(s: &str) -> &Self {
|
|
||||||
unsafe { std::mem::transmute(s) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn from_owned(s: Box<str>) -> Box<Self> {
|
|
||||||
unsafe { Box::from_raw(Box::into_raw(s) as _) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn from_rc(s: std::rc::Rc<str>) -> std::rc::Rc<Self> {
|
|
||||||
unsafe { std::rc::Rc::from_raw(std::rc::Rc::into_raw(s) as _) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn from_arc(s: std::sync::Arc<str>) -> std::sync::Arc<Self> {
|
|
||||||
unsafe { std::sync::Arc::from_raw(std::sync::Arc::into_raw(s) as _) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn into_owned(self: Box<Self>) -> Box<str> {
|
|
||||||
unsafe { Box::from_raw(Box::into_raw(self) as _) }
|
|
||||||
}
|
|
||||||
|
|
||||||
doc_concat! {
|
|
||||||
#[doc = concat!("Creates a string slice from this `", stringify!($id), "`.")]
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
doc_concat! {
|
|
||||||
#[doc = concat!("Creates a byte slice from this `", stringify!($id), "`.")]
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
self.0.as_bytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Box<$id> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
(**self).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl ToOwned for $id {
|
|
||||||
// type Owned = $owned;
|
|
||||||
|
|
||||||
// fn to_owned(&self) -> Self::Owned {
|
|
||||||
// Self::from_owned(self.0.into()).into()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO swap below with above after codebase has been converted
|
|
||||||
// to not use `to_owned` as equivalent to "into Box"
|
|
||||||
impl ToOwned for $id {
|
|
||||||
type Owned = Box<$id>;
|
|
||||||
|
|
||||||
fn to_owned(&self) -> Self::Owned {
|
|
||||||
Self::from_owned(self.0.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for $id {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
self.as_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for Box<$id> {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
self.as_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&$id> for Box<$id> {
|
|
||||||
fn from(id: &$id) -> Self {
|
|
||||||
$id::from_owned(id.0.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&$id> for std::rc::Rc<$id> {
|
|
||||||
fn from(s: &$id) -> std::rc::Rc<$id> {
|
|
||||||
let rc = std::rc::Rc::<str>::from(s.as_str());
|
|
||||||
<$id>::from_rc(rc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&$id> for std::sync::Arc<$id> {
|
|
||||||
fn from(s: &$id) -> std::sync::Arc<$id> {
|
|
||||||
let arc = std::sync::Arc::<str>::from(s.as_str());
|
|
||||||
<$id>::from_arc(arc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<$id> for Box<$id> {
|
|
||||||
fn eq(&self, other: &$id) -> bool {
|
|
||||||
self.as_str() == other.as_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<&'_ $id> for Box<$id> {
|
|
||||||
fn eq(&self, other: &&$id) -> bool {
|
|
||||||
self.as_str() == other.as_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<Box<$id>> for $id {
|
|
||||||
fn eq(&self, other: &Box<$id>) -> bool {
|
|
||||||
self.as_str() == other.as_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<Box<$id>> for &'_ $id {
|
|
||||||
fn eq(&self, other: &Box<$id>) -> bool {
|
|
||||||
self.as_str() == other.as_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for $id {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
<str as std::fmt::Debug>::fmt(self.as_str(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for $id {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl serde::Serialize for $id {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
serializer.serialize_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
partial_eq_string!($id);
|
|
||||||
partial_eq_string!(Box<$id>); // todo: Remove when all instances of Box have been converted to Owned
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! opaque_identifier {
|
|
||||||
($id:ident, $owned:ident) => {
|
|
||||||
opaque_identifier_common_impls!($id, $owned);
|
|
||||||
|
|
||||||
impl<'a> From<&'a str> for &'a $id {
|
|
||||||
fn from(s: &'a str) -> Self {
|
|
||||||
$id::from_borrowed(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Box<$id> {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
$id::from_owned(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<str>> for Box<$id> {
|
|
||||||
fn from(s: Box<str>) -> Self {
|
|
||||||
$id::from_owned(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Box<$id> {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
$id::from_owned(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<$id>> for Box<str> {
|
|
||||||
fn from(id: Box<$id>) -> Self {
|
|
||||||
id.into_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<$id>> for String {
|
|
||||||
fn from(id: Box<$id>) -> Self {
|
|
||||||
id.into_owned().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for Box<$id> {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
Box::<str>::deserialize(deserializer).map($id::from_owned)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de> serde::Deserialize<'de> for $owned {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
Box::<str>::deserialize(deserializer).map($id::from_owned).map(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! opaque_identifier_validated {
|
|
||||||
($id:ident, $owned:ident, $validate_id:expr) => {
|
|
||||||
impl $id {
|
|
||||||
#[rustfmt::skip]
|
|
||||||
doc_concat! {
|
|
||||||
#[doc = concat!("\
|
|
||||||
Try parsing a `&str` into a `Box<", stringify!($id), ">`.\n\
|
|
||||||
\n\
|
|
||||||
The same can also be done using `FromStr`, `TryFrom` or `TryInto`.\n\
|
|
||||||
This function is simply more constrained and thus useful in generic contexts.\
|
|
||||||
")]
|
|
||||||
pub fn parse(
|
|
||||||
s: impl AsRef<str> + Into<Box<str>>,
|
|
||||||
) -> Result<Box<Self>, crate::IdParseError> {
|
|
||||||
$validate_id(s.as_ref())?;
|
|
||||||
Ok($id::from_owned(s.into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
doc_concat! {
|
|
||||||
#[doc = concat!("Try parsing a `&str` into an `Rc<", stringify!($id), ">`.")]
|
|
||||||
pub fn parse_rc(
|
|
||||||
s: impl AsRef<str> + Into<std::rc::Rc<str>>,
|
|
||||||
) -> Result<std::rc::Rc<Self>, crate::IdParseError> {
|
|
||||||
$validate_id(s.as_ref())?;
|
|
||||||
Ok($id::from_rc(s.into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
doc_concat! {
|
|
||||||
#[doc = concat!("Try parsing a `&str` into an `Arc<", stringify!($id), ">`.")]
|
|
||||||
pub fn parse_arc(
|
|
||||||
s: impl AsRef<str> + Into<std::sync::Arc<str>>,
|
|
||||||
) -> Result<std::sync::Arc<Self>, crate::IdParseError> {
|
|
||||||
$validate_id(s.as_ref())?;
|
|
||||||
Ok($id::from_arc(s.into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opaque_identifier_common_impls!($id, $owned);
|
|
||||||
|
|
||||||
impl From<Box<$id>> for String {
|
|
||||||
fn from(id: Box<$id>) -> Self {
|
|
||||||
id.into_owned().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for Box<$id> {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
use serde::de::Error;
|
|
||||||
|
|
||||||
let s = String::deserialize(deserializer)?;
|
|
||||||
|
|
||||||
match $id::parse(s) {
|
|
||||||
Ok(o) => Ok(o),
|
|
||||||
Err(e) => Err(D::Error::custom(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de> serde::Deserialize<'de> for $owned {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
use serde::de::Error;
|
|
||||||
|
|
||||||
let s = String::deserialize(deserializer)?;
|
|
||||||
|
|
||||||
match $id::parse(s) {
|
|
||||||
Ok(o) => Ok(o.into()),
|
|
||||||
Err(e) => Err(D::Error::custom(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> std::convert::TryFrom<&'a str> for &'a $id {
|
|
||||||
type Error = crate::IdParseError;
|
|
||||||
|
|
||||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
|
||||||
$validate_id(s)?;
|
|
||||||
Ok($id::from_borrowed(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::str::FromStr for Box<$id> {
|
|
||||||
type Err = crate::IdParseError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
$id::parse(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::TryFrom<&str> for Box<$id> {
|
|
||||||
type Error = crate::IdParseError;
|
|
||||||
|
|
||||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
|
||||||
$id::parse(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::TryFrom<String> for Box<$id> {
|
|
||||||
type Error = crate::IdParseError;
|
|
||||||
|
|
||||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
|
||||||
$id::parse(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -5,6 +5,7 @@
|
|||||||
use std::num::NonZeroU8;
|
use std::num::NonZeroU8;
|
||||||
|
|
||||||
use ruma_identifiers_validation::{error::MxcUriError, mxc_uri::validate};
|
use ruma_identifiers_validation::{error::MxcUriError, mxc_uri::validate};
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
use super::ServerName;
|
use super::ServerName;
|
||||||
|
|
||||||
@ -15,13 +16,9 @@ type Result<T, E = MxcUriError> = std::result::Result<T, E>;
|
|||||||
/// [MXC URI]: https://spec.matrix.org/v1.2/client-server-api/#matrix-content-mxc-uris
|
/// [MXC URI]: https://spec.matrix.org/v1.2/client-server-api/#matrix-content-mxc-uris
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
pub struct MxcUri(str);
|
pub struct MxcUri(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedMxcUri, MxcUri);
|
|
||||||
|
|
||||||
opaque_identifier!(MxcUri, OwnedMxcUri);
|
|
||||||
|
|
||||||
impl MxcUri {
|
impl MxcUri {
|
||||||
/// If this is a valid MXC URI, returns the media ID.
|
/// If this is a valid MXC URI, returns the media ID.
|
||||||
pub fn media_id(&self) -> Result<&str> {
|
pub fn media_id(&self) -> Result<&str> {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! Matrix room alias identifiers.
|
//! Matrix room alias identifiers.
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
use super::{matrix_uri::UriAction, server_name::ServerName, EventId, MatrixToUri, MatrixUri};
|
use super::{matrix_uri::UriAction, server_name::ServerName, EventId, MatrixToUri, MatrixUri};
|
||||||
|
|
||||||
/// A Matrix [room alias ID].
|
/// A Matrix [room alias ID].
|
||||||
@ -15,17 +17,10 @@ use super::{matrix_uri::UriAction, server_name::ServerName, EventId, MatrixToUri
|
|||||||
///
|
///
|
||||||
/// [room alias ID]: https://spec.matrix.org/v1.2/appendices/#room-aliases
|
/// [room alias ID]: https://spec.matrix.org/v1.2/appendices/#room-aliases
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::room_alias_id::validate)]
|
||||||
pub struct RoomAliasId(str);
|
pub struct RoomAliasId(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedRoomAliasId, RoomAliasId);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(
|
|
||||||
RoomAliasId,
|
|
||||||
OwnedRoomAliasId,
|
|
||||||
ruma_identifiers_validation::room_alias_id::validate
|
|
||||||
);
|
|
||||||
|
|
||||||
impl RoomAliasId {
|
impl RoomAliasId {
|
||||||
/// Returns the room's alias.
|
/// Returns the room's alias.
|
||||||
pub fn alias(&self) -> &str {
|
pub fn alias(&self) -> &str {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! Matrix room identifiers.
|
//! Matrix room identifiers.
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
use super::{matrix_uri::UriAction, EventId, MatrixToUri, MatrixUri, ServerName};
|
use super::{matrix_uri::UriAction, EventId, MatrixToUri, MatrixUri, ServerName};
|
||||||
|
|
||||||
/// A Matrix [room ID].
|
/// A Matrix [room ID].
|
||||||
@ -15,13 +17,10 @@ use super::{matrix_uri::UriAction, EventId, MatrixToUri, MatrixUri, ServerName};
|
|||||||
///
|
///
|
||||||
/// [room ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
|
/// [room ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::room_id::validate)]
|
||||||
pub struct RoomId(str);
|
pub struct RoomId(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedRoomId, RoomId);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(RoomId, OwnedRoomId, ruma_identifiers_validation::room_id::validate);
|
|
||||||
|
|
||||||
impl RoomId {
|
impl RoomId {
|
||||||
/// Attempts to generate a `RoomId` for the given origin server with a localpart consisting of
|
/// Attempts to generate a `RoomId` for the given origin server with a localpart consisting of
|
||||||
/// 18 random ASCII characters.
|
/// 18 random ASCII characters.
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
//! Matrix room name.
|
//! Matrix room name.
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
/// The name of a room.
|
/// The name of a room.
|
||||||
///
|
///
|
||||||
/// It can't exceed 255 bytes or be empty.
|
/// It can't exceed 255 bytes or be empty.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::room_name::validate)]
|
||||||
pub struct RoomName(str);
|
pub struct RoomName(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedRoomName, RoomName);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(
|
|
||||||
RoomName,
|
|
||||||
OwnedRoomName,
|
|
||||||
ruma_identifiers_validation::room_name::validate
|
|
||||||
);
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
use std::{convert::TryFrom, hint::unreachable_unchecked};
|
use std::{convert::TryFrom, hint::unreachable_unchecked};
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
use super::{server_name::ServerName, RoomAliasId, RoomId};
|
use super::{server_name::ServerName, RoomAliasId, RoomId};
|
||||||
|
|
||||||
/// A Matrix [room ID] or a Matrix [room alias ID].
|
/// A Matrix [room ID] or a Matrix [room alias ID].
|
||||||
@ -24,17 +26,10 @@ use super::{server_name::ServerName, RoomAliasId, RoomId};
|
|||||||
/// [room ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
|
/// [room ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
|
||||||
/// [room alias ID]: https://spec.matrix.org/v1.2/appendices/#room-aliases
|
/// [room alias ID]: https://spec.matrix.org/v1.2/appendices/#room-aliases
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::room_id_or_alias_id::validate)]
|
||||||
pub struct RoomOrAliasId(str);
|
pub struct RoomOrAliasId(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedRoomOrAliasId, RoomOrAliasId);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(
|
|
||||||
RoomOrAliasId,
|
|
||||||
OwnedRoomOrAliasId,
|
|
||||||
ruma_identifiers_validation::room_id_or_alias_id::validate
|
|
||||||
);
|
|
||||||
|
|
||||||
impl RoomOrAliasId {
|
impl RoomOrAliasId {
|
||||||
/// Returns the local part (everything after the `!` or `#` and before the first colon).
|
/// Returns the local part (everything after the `!` or `#` and before the first colon).
|
||||||
pub fn localpart(&self) -> &str {
|
pub fn localpart(&self) -> &str {
|
||||||
|
@ -2,23 +2,18 @@
|
|||||||
|
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
/// A Matrix-spec compliant [server name].
|
/// A Matrix-spec compliant [server name].
|
||||||
///
|
///
|
||||||
/// It consists of a host and an optional port (separated by a colon if present).
|
/// It consists of a host and an optional port (separated by a colon if present).
|
||||||
///
|
///
|
||||||
/// [server name]: https://spec.matrix.org/v1.2/appendices/#server-name
|
/// [server name]: https://spec.matrix.org/v1.2/appendices/#server-name
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::server_name::validate)]
|
||||||
pub struct ServerName(str);
|
pub struct ServerName(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedServerName, ServerName);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(
|
|
||||||
ServerName,
|
|
||||||
OwnedServerName,
|
|
||||||
ruma_identifiers_validation::server_name::validate
|
|
||||||
);
|
|
||||||
|
|
||||||
impl ServerName {
|
impl ServerName {
|
||||||
/// Returns the host of the server name.
|
/// Returns the host of the server name.
|
||||||
///
|
///
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
//! Matrix session ID.
|
//! Matrix session ID.
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
/// A session ID.
|
/// A session ID.
|
||||||
///
|
///
|
||||||
/// Session IDs in Matrix are opaque character sequences of `[0-9a-zA-Z.=_-]`. Their length must
|
/// Session IDs in Matrix are opaque character sequences of `[0-9a-zA-Z.=_-]`. Their length must
|
||||||
/// must not exceed 255 characters.
|
/// must not exceed 255 characters.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::session_id::validate)]
|
||||||
pub struct SessionId(str);
|
pub struct SessionId(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedSessionId, SessionId);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(
|
|
||||||
SessionId,
|
|
||||||
OwnedSessionId,
|
|
||||||
ruma_identifiers_validation::session_id::validate
|
|
||||||
);
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
/// A Matrix transaction ID.
|
/// A Matrix transaction ID.
|
||||||
///
|
///
|
||||||
/// Transaction IDs in Matrix are opaque strings. This type is provided simply for its semantic
|
/// Transaction IDs in Matrix are opaque strings. This type is provided simply for its semantic
|
||||||
@ -7,7 +9,7 @@
|
|||||||
/// `TransactionId::new()` to generate a random one. If that function is not available for you, you
|
/// `TransactionId::new()` to generate a random one. If that function is not available for you, you
|
||||||
/// need to activate this crate's `rand` Cargo feature.
|
/// need to activate this crate's `rand` Cargo feature.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
pub struct TransactionId(str);
|
pub struct TransactionId(str);
|
||||||
|
|
||||||
impl TransactionId {
|
impl TransactionId {
|
||||||
@ -21,7 +23,3 @@ impl TransactionId {
|
|||||||
Self::from_owned(id.to_simple().to_string().into_boxed_str())
|
Self::from_owned(id.to_simple().to_string().into_boxed_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
owned_identifier!(OwnedTransactionId, TransactionId);
|
|
||||||
|
|
||||||
opaque_identifier!(TransactionId, OwnedTransactionId);
|
|
||||||
|
@ -17,13 +17,10 @@ use super::{matrix_uri::UriAction, IdParseError, MatrixToUri, MatrixUri, ServerN
|
|||||||
///
|
///
|
||||||
/// [user ID]: https://spec.matrix.org/v1.2/appendices/#user-identifiers
|
/// [user ID]: https://spec.matrix.org/v1.2/appendices/#user-identifiers
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
#[ruma_id(validate = ruma_identifiers_validation::user_id::validate)]
|
||||||
pub struct UserId(str);
|
pub struct UserId(str);
|
||||||
|
|
||||||
owned_identifier!(OwnedUserId, UserId);
|
|
||||||
|
|
||||||
opaque_identifier_validated!(UserId, OwnedUserId, ruma_identifiers_validation::user_id::validate);
|
|
||||||
|
|
||||||
impl UserId {
|
impl UserId {
|
||||||
/// 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.
|
||||||
@ -150,6 +147,7 @@ impl UserId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use ruma_identifiers_validation::user_id::localpart_is_fully_conforming;
|
pub use ruma_identifiers_validation::user_id::localpart_is_fully_conforming;
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
//! Methods and types for generating identifiers.
|
//! Methods and types for generating identifiers.
|
||||||
|
|
||||||
use syn::{parse::Parse, LitStr, Path, Token};
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote, ToTokens};
|
||||||
|
use syn::{
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
punctuated::Punctuated,
|
||||||
|
Ident, ItemStruct, LitStr, Path, Token,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct IdentifierInput {
|
pub struct IdentifierInput {
|
||||||
pub dollar_crate: Path,
|
pub dollar_crate: Path,
|
||||||
@ -8,7 +14,7 @@ pub struct IdentifierInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for IdentifierInput {
|
impl Parse for IdentifierInput {
|
||||||
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
|
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||||
let dollar_crate = input.parse()?;
|
let dollar_crate = input.parse()?;
|
||||||
let _: Token![,] = input.parse()?;
|
let _: Token![,] = input.parse()?;
|
||||||
let id = input.parse()?;
|
let id = input.parse()?;
|
||||||
@ -16,3 +22,477 @@ impl Parse for IdentifierInput {
|
|||||||
Ok(Self { dollar_crate, id })
|
Ok(Self { dollar_crate, id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expand_id_zst(input: ItemStruct) -> syn::Result<TokenStream> {
|
||||||
|
let id = &input.ident;
|
||||||
|
let owned = format_ident!("Owned{}", id);
|
||||||
|
|
||||||
|
let owned_decl = expand_owned_id(id, &owned);
|
||||||
|
|
||||||
|
let meta = input.attrs.iter().filter(|attr| attr.path.is_ident("ruma_id")).try_fold(
|
||||||
|
IdZstMeta::default(),
|
||||||
|
|meta, attr| {
|
||||||
|
let list: Punctuated<IdZstMeta, Token![,]> =
|
||||||
|
attr.parse_args_with(Punctuated::parse_terminated)?;
|
||||||
|
|
||||||
|
list.into_iter().try_fold(meta, IdZstMeta::merge)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let extra_impls = if let Some(validate) = meta.validate {
|
||||||
|
expand_checked_impls(id, &owned, validate)
|
||||||
|
} else {
|
||||||
|
expand_unchecked_impls(id, &owned)
|
||||||
|
};
|
||||||
|
|
||||||
|
let as_str_docs = format!("Creates a string slice from this `{}`.", id);
|
||||||
|
let as_bytes_docs = format!("Creates a byte slice from this `{}`.", id);
|
||||||
|
|
||||||
|
let partial_eq_string = expand_partial_eq_string(id);
|
||||||
|
// FIXME: Remove?
|
||||||
|
let box_partial_eq_string = expand_partial_eq_string(quote! { Box<#id> });
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
#owned_decl
|
||||||
|
|
||||||
|
impl #id {
|
||||||
|
pub(super) fn from_borrowed(s: &str) -> &Self {
|
||||||
|
unsafe { std::mem::transmute(s) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn from_owned(s: Box<str>) -> Box<Self> {
|
||||||
|
unsafe { Box::from_raw(Box::into_raw(s) as _) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn from_rc(s: std::rc::Rc<str>) -> std::rc::Rc<Self> {
|
||||||
|
unsafe { std::rc::Rc::from_raw(std::rc::Rc::into_raw(s) as _) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn from_arc(s: std::sync::Arc<str>) -> std::sync::Arc<Self> {
|
||||||
|
unsafe { std::sync::Arc::from_raw(std::sync::Arc::into_raw(s) as _) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn into_owned(self: Box<Self>) -> Box<str> {
|
||||||
|
unsafe { Box::from_raw(Box::into_raw(self) as _) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = #as_str_docs]
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = #as_bytes_docs]
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
self.0.as_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<#id> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
(**self).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToOwned for #id {
|
||||||
|
type Owned = Box<#id>;
|
||||||
|
|
||||||
|
fn to_owned(&self) -> Self::Owned {
|
||||||
|
Self::from_owned(self.0.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for #id {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for Box<#id> {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Box<#id>> for String {
|
||||||
|
fn from(id: Box<#id>) -> Self {
|
||||||
|
id.into_owned().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&#id> for Box<#id> {
|
||||||
|
fn from(id: &#id) -> Self {
|
||||||
|
#id::from_owned(id.0.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&#id> for std::rc::Rc<#id> {
|
||||||
|
fn from(s: &#id) -> std::rc::Rc<#id> {
|
||||||
|
let rc = std::rc::Rc::<str>::from(s.as_str());
|
||||||
|
<#id>::from_rc(rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&#id> for std::sync::Arc<#id> {
|
||||||
|
fn from(s: &#id) -> std::sync::Arc<#id> {
|
||||||
|
let arc = std::sync::Arc::<str>::from(s.as_str());
|
||||||
|
<#id>::from_arc(arc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<#id> for Box<#id> {
|
||||||
|
fn eq(&self, other: &#id) -> bool {
|
||||||
|
self.as_str() == other.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<&'_ #id> for Box<#id> {
|
||||||
|
fn eq(&self, other: &&#id) -> bool {
|
||||||
|
self.as_str() == other.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<Box<#id>> for #id {
|
||||||
|
fn eq(&self, other: &Box<#id>) -> bool {
|
||||||
|
self.as_str() == other.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<Box<#id>> for &'_ #id {
|
||||||
|
fn eq(&self, other: &Box<#id>) -> bool {
|
||||||
|
self.as_str() == other.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for #id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
<str as std::fmt::Debug>::fmt(self.as_str(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for #id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl serde::Serialize for #id {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#partial_eq_string
|
||||||
|
#box_partial_eq_string
|
||||||
|
#extra_impls
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_owned_id(id: &Ident, owned: &Ident) -> TokenStream {
|
||||||
|
let doc_header = format!("Owned variant of {}", id);
|
||||||
|
let partial_eq_string = expand_partial_eq_string(owned);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[doc = #doc_header]
|
||||||
|
///
|
||||||
|
/// The wrapper type for this type is variable, by default it'll use [`Box`],
|
||||||
|
/// but you can change that by setting "`--cfg=ruma_identifiers_storage=...`" using
|
||||||
|
/// `RUSTFLAGS` or `.cargo/config.toml` (under `[build]` -> `rustflags = ["..."]`)
|
||||||
|
/// to the following;
|
||||||
|
/// - `ruma_identifiers_storage="Arc"` to use [`Arc`](std::sync::Arc) as a wrapper type.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct #owned {
|
||||||
|
#[cfg(not(any(ruma_identifiers_storage = "Arc")))]
|
||||||
|
inner: Box<#id>,
|
||||||
|
#[cfg(ruma_identifiers_storage = "Arc")]
|
||||||
|
inner: std::sync::Arc<#id>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<#id> for #owned {
|
||||||
|
fn as_ref(&self) -> &#id {
|
||||||
|
&*self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for #owned {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
(*self.inner).as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for #owned {
|
||||||
|
type Target = #id;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::borrow::Borrow<#id> for #owned {
|
||||||
|
fn borrow(&self) -> &#id {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'_ #id> for #owned {
|
||||||
|
fn from(id: &#id) -> #owned {
|
||||||
|
#owned { inner: id.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Box<#id>> for #owned {
|
||||||
|
fn from(b: Box<#id>) -> #owned {
|
||||||
|
Self { inner: b.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::sync::Arc<#id>> for #owned {
|
||||||
|
fn from(a: std::sync::Arc<#id>) -> #owned {
|
||||||
|
Self {
|
||||||
|
#[cfg(not(any(ruma_identifiers_storage = "Arc")))]
|
||||||
|
inner: a.as_ref().into(),
|
||||||
|
#[cfg(ruma_identifiers_storage = "Arc")]
|
||||||
|
inner: a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl serde::Serialize for #owned {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#partial_eq_string
|
||||||
|
|
||||||
|
impl PartialEq<Box<#id>> for #owned {
|
||||||
|
fn eq(&self, other: &Box<#id>) -> bool {
|
||||||
|
AsRef::<#id>::as_ref(self) == AsRef::<#id>::as_ref(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<#owned> for Box<#id> {
|
||||||
|
fn eq(&self, other: &#owned) -> bool {
|
||||||
|
AsRef::<#id>::as_ref(self) == AsRef::<#id>::as_ref(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_checked_impls(id: &Ident, owned: &Ident, validate: Path) -> TokenStream {
|
||||||
|
let parse_doc_header = format!("Try parsing a `&str` into a `Box<{}>`.", id);
|
||||||
|
let parse_rc_docs = format!("Try parsing a `&str` into an `Rc<{}>`.", id);
|
||||||
|
let parse_arc_docs = format!("Try parsing a `&str` into an `Arc<{}>`.", id);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl #id {
|
||||||
|
#[doc = #parse_doc_header]
|
||||||
|
///
|
||||||
|
/// The same can also be done using `FromStr`, `TryFrom` or `TryInto`.
|
||||||
|
/// This function is simply more constrained and thus useful in generic contexts.
|
||||||
|
pub fn parse(
|
||||||
|
s: impl AsRef<str> + Into<Box<str>>,
|
||||||
|
) -> Result<Box<Self>, crate::IdParseError> {
|
||||||
|
#validate(s.as_ref())?;
|
||||||
|
Ok(#id::from_owned(s.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = #parse_rc_docs]
|
||||||
|
pub fn parse_rc(
|
||||||
|
s: impl AsRef<str> + Into<std::rc::Rc<str>>,
|
||||||
|
) -> Result<std::rc::Rc<Self>, crate::IdParseError> {
|
||||||
|
#validate(s.as_ref())?;
|
||||||
|
Ok(#id::from_rc(s.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = #parse_arc_docs]
|
||||||
|
pub fn parse_arc(
|
||||||
|
s: impl AsRef<str> + Into<std::sync::Arc<str>>,
|
||||||
|
) -> Result<std::sync::Arc<Self>, crate::IdParseError> {
|
||||||
|
#validate(s.as_ref())?;
|
||||||
|
Ok(#id::from_arc(s.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for Box<#id> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde::de::Error;
|
||||||
|
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
match #id::parse(s) {
|
||||||
|
Ok(o) => Ok(o),
|
||||||
|
Err(e) => Err(D::Error::custom(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for #owned {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde::de::Error;
|
||||||
|
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
match #id::parse(s) {
|
||||||
|
Ok(o) => Ok(o.into()),
|
||||||
|
Err(e) => Err(D::Error::custom(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::convert::TryFrom<&'a str> for &'a #id {
|
||||||
|
type Error = crate::IdParseError;
|
||||||
|
|
||||||
|
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||||
|
#validate(s)?;
|
||||||
|
Ok(#id::from_borrowed(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::str::FromStr for Box<#id> {
|
||||||
|
type Err = crate::IdParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
#id::parse(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::TryFrom<&str> for Box<#id> {
|
||||||
|
type Error = crate::IdParseError;
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
#id::parse(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::TryFrom<String> for Box<#id> {
|
||||||
|
type Error = crate::IdParseError;
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
#id::parse(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_unchecked_impls(id: &Ident, owned: &Ident) -> TokenStream {
|
||||||
|
quote! {
|
||||||
|
impl<'a> From<&'a str> for &'a #id {
|
||||||
|
fn from(s: &'a str) -> Self {
|
||||||
|
#id::from_borrowed(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Box<#id> {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
#id::from_owned(s.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Box<str>> for Box<#id> {
|
||||||
|
fn from(s: Box<str>) -> Self {
|
||||||
|
#id::from_owned(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Box<#id> {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
#id::from_owned(s.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Box<#id>> for Box<str> {
|
||||||
|
fn from(id: Box<#id>) -> Self {
|
||||||
|
id.into_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for Box<#id> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Box::<str>::deserialize(deserializer).map(#id::from_owned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for #owned {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
// FIXME: Deserialize inner, convert that
|
||||||
|
Box::<str>::deserialize(deserializer).map(#id::from_owned).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_partial_eq_string(id: impl ToTokens) -> TokenStream {
|
||||||
|
fn single_impl(lhs: impl ToTokens, rhs: impl ToTokens) -> TokenStream {
|
||||||
|
quote! {
|
||||||
|
impl PartialEq<#rhs> for #lhs {
|
||||||
|
fn eq(&self, other: &#rhs) -> bool {
|
||||||
|
AsRef::<str>::as_ref(self)
|
||||||
|
== AsRef::<str>::as_ref(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = &id;
|
||||||
|
|
||||||
|
let mut res = TokenStream::new();
|
||||||
|
res.extend(single_impl(id, quote! { str }));
|
||||||
|
res.extend(single_impl(id, quote! { &str }));
|
||||||
|
res.extend(single_impl(id, quote! { String }));
|
||||||
|
res.extend(single_impl(quote! { str }, id));
|
||||||
|
res.extend(single_impl(quote! { &str }, id));
|
||||||
|
res.extend(single_impl(quote! { String }, id));
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
mod kw {
|
||||||
|
syn::custom_keyword!(validate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct IdZstMeta {
|
||||||
|
validate: Option<Path>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdZstMeta {
|
||||||
|
fn merge(self, other: IdZstMeta) -> syn::Result<Self> {
|
||||||
|
let validate = match (self.validate, other.validate) {
|
||||||
|
(None, None) => None,
|
||||||
|
(Some(val), None) | (None, Some(val)) => Some(val),
|
||||||
|
(Some(a), Some(b)) => {
|
||||||
|
let mut error = syn::Error::new_spanned(b, "duplicate attribute argument");
|
||||||
|
error.combine(syn::Error::new_spanned(a, "note: first one here"));
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self { validate })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for IdZstMeta {
|
||||||
|
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||||
|
let _: kw::validate = input.parse()?;
|
||||||
|
let _: Token![=] = input.parse()?;
|
||||||
|
let validate = Some(input.parse()?);
|
||||||
|
Ok(Self { validate })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
use identifiers::expand_id_zst;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2 as pm2;
|
use proc_macro2 as pm2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
@ -13,7 +14,7 @@ use ruma_identifiers_validation::{
|
|||||||
device_key_id, event_id, key_id, mxc_uri, room_alias_id, room_id, room_version_id, server_name,
|
device_key_id, event_id, key_id, mxc_uri, room_alias_id, room_id, room_version_id, server_name,
|
||||||
user_id,
|
user_id,
|
||||||
};
|
};
|
||||||
use syn::{parse_macro_input, DeriveInput, ItemEnum};
|
use syn::{parse_macro_input, DeriveInput, ItemEnum, ItemStruct};
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod events;
|
mod events;
|
||||||
@ -116,6 +117,13 @@ pub fn derive_from_event_to_enum(input: TokenStream) -> TokenStream {
|
|||||||
expand_from_impls_derived(input).into()
|
expand_from_impls_derived(input).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate methods and trait impl's for ZST identifier type.
|
||||||
|
#[proc_macro_derive(IdZst, attributes(ruma_id))]
|
||||||
|
pub fn derive_id_zst(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as ItemStruct);
|
||||||
|
expand_id_zst(input).unwrap_or_else(syn::Error::into_compile_error).into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Compile-time checked `DeviceKeyId` construction.
|
/// Compile-time checked `DeviceKeyId` construction.
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn device_key_id(input: TokenStream) -> TokenStream {
|
pub fn device_key_id(input: TokenStream) -> TokenStream {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user