macros: Expand IdZst derive to generic types

… and use it for KeyId.
This commit is contained in:
Jonas Platte 2022-04-14 13:57:02 +02:00
parent 5d8f6748e6
commit ae095cc037
No known key found for this signature in database
GPG Key ID: BBA95679259D342F
2 changed files with 148 additions and 309 deletions

View File

@ -1,18 +1,18 @@
use std::{
cmp::Ordering,
convert::TryFrom,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
rc::Rc,
str::FromStr,
sync::Arc,
};
use super::{crypto_algorithms::SigningKeyAlgorithm, DeviceId, IdParseError, KeyName};
use ruma_macros::IdZst;
use super::{crypto_algorithms::SigningKeyAlgorithm, DeviceId, KeyName};
/// A key algorithm and key name delimited by a colon.
#[repr(transparent)]
#[derive(IdZst)]
#[ruma_id(validate = ruma_identifiers_validation::key_id::validate)]
pub struct KeyId<A, K: ?Sized>(PhantomData<(A, K)>, str);
impl<A, K: ?Sized> KeyId<A, K> {
@ -49,28 +49,6 @@ impl<A, K: ?Sized> KeyId<A, K> {
self.as_str()[self.colon_idx() + 1..].into()
}
/// Creates a string slice from this `KeyId`.
pub fn as_str(&self) -> &str {
&self.1
}
/// Creates a byte slice from this `KeyId`.
pub fn as_bytes(&self) -> &[u8] {
self.1.as_bytes()
}
fn from_borrowed(s: &str) -> &Self {
unsafe { std::mem::transmute(s) }
}
fn from_box(s: Box<str>) -> Box<Self> {
unsafe { Box::from_raw(Box::into_raw(s) as _) }
}
fn into_owned(self: Box<Self>) -> Box<str> {
unsafe { Box::from_raw(Box::into_raw(self) as _) }
}
fn colon_idx(&self) -> usize {
self.as_str().find(':').unwrap()
}
@ -85,88 +63,8 @@ pub type ServerSigningKeyId = SigningKeyId<KeyName>;
/// Algorithm + key name for device keys.
pub type DeviceSigningKeyId = SigningKeyId<DeviceId>;
impl<A, K: ?Sized> Clone for Box<KeyId<A, K>> {
fn clone(&self) -> Self {
(**self).to_owned()
}
}
impl<A, K: ?Sized> ToOwned for KeyId<A, K> {
type Owned = Box<KeyId<A, K>>;
fn to_owned(&self) -> Self::Owned {
Self::from_box(self.1.into())
}
}
impl<A, K: ?Sized> From<&KeyId<A, K>> for Box<KeyId<A, K>> {
fn from(id: &KeyId<A, K>) -> Self {
id.to_owned()
}
}
impl<A, K: ?Sized> AsRef<str> for Box<KeyId<A, K>> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<A, K: ?Sized> From<&KeyId<A, K>> for Rc<KeyId<A, K>> {
fn from(s: &KeyId<A, K>) -> Rc<KeyId<A, K>> {
let rc = Rc::<str>::from(s.as_str());
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const KeyId<A, K>) }
}
}
impl<A, K: ?Sized> From<&KeyId<A, K>> for Arc<KeyId<A, K>> {
fn from(s: &KeyId<A, K>) -> Arc<KeyId<A, K>> {
let arc = Arc::<str>::from(s.as_str());
unsafe { Arc::from_raw(Arc::into_raw(arc) as *const KeyId<A, K>) }
}
}
impl<A, K: ?Sized> PartialEq<KeyId<A, K>> for Box<KeyId<A, K>> {
fn eq(&self, other: &KeyId<A, K>) -> bool {
self.as_str() == other.as_str()
}
}
impl<A, K: ?Sized> PartialEq<&'_ KeyId<A, K>> for Box<KeyId<A, K>> {
fn eq(&self, other: &&KeyId<A, K>) -> bool {
self.as_str() == other.as_str()
}
}
impl<A, K: ?Sized> PartialEq<Box<KeyId<A, K>>> for KeyId<A, K> {
fn eq(&self, other: &Box<KeyId<A, K>>) -> bool {
self.as_str() == other.as_str()
}
}
impl<A, K: ?Sized> PartialEq<Box<KeyId<A, K>>> for &'_ KeyId<A, K> {
fn eq(&self, other: &Box<KeyId<A, K>>) -> bool {
self.as_str() == other.as_str()
}
}
impl<A, K: ?Sized> AsRef<str> for KeyId<A, K> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<A, K: ?Sized> fmt::Display for KeyId<A, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
impl<A, K: ?Sized> fmt::Debug for KeyId<A, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
// The following impls are usually derived using the std macros.
// They are implemented manually here to avoid unnecessary bounds.
impl<A, K: ?Sized> PartialEq for KeyId<A, K> {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
@ -192,95 +90,3 @@ impl<A, K: ?Sized> Hash for KeyId<A, K> {
self.as_str().hash(state);
}
}
impl<A, K: ?Sized> serde::Serialize for KeyId<A, K> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<A, K: ?Sized> From<Box<KeyId<A, K>>> for String {
fn from(id: Box<KeyId<A, K>>) -> Self {
id.into_owned().into()
}
}
impl<'de, A, K: ?Sized> serde::Deserialize<'de> for Box<KeyId<A, K>> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
let s = String::deserialize(deserializer)?;
match try_from(s) {
Ok(o) => Ok(o),
Err(e) => Err(D::Error::custom(e)),
}
}
}
fn try_from<S, A, K: ?Sized>(s: S) -> Result<Box<KeyId<A, K>>, IdParseError>
where
S: AsRef<str> + Into<Box<str>>,
{
ruma_identifiers_validation::key_id::validate(s.as_ref())?;
Ok(KeyId::from_box(s.into()))
}
impl<'a, A, K: ?Sized> TryFrom<&'a str> for &'a KeyId<A, K> {
type Error = IdParseError;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
(ruma_identifiers_validation::key_id::validate)(s)?;
Ok(KeyId::from_borrowed(s))
}
}
impl<A, K: ?Sized> FromStr for Box<KeyId<A, K>> {
type Err = IdParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
try_from(s)
}
}
impl<A, K: ?Sized> TryFrom<&str> for Box<KeyId<A, K>> {
type Error = IdParseError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
try_from(s)
}
}
impl<A, K: ?Sized> TryFrom<String> for Box<KeyId<A, K>> {
type Error = IdParseError;
fn try_from(s: String) -> Result<Self, Self::Error> {
try_from(s)
}
}
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]
partial_eq_string!(KeyId<A, K> [A, K]);

View File

@ -1,11 +1,11 @@
//! Methods and types for generating identifiers.
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
Ident, ItemStruct, LitStr, Path, Token,
Fields, ImplGenerics, Index, ItemStruct, LitStr, Path, Token,
};
pub struct IdentifierInput {
@ -25,9 +25,8 @@ impl Parse for IdentifierInput {
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 owned_decl = expand_owned_id(&input);
let meta = input.attrs.iter().filter(|attr| attr.path.is_ident("ruma_id")).try_fold(
IdZstMeta::default(),
@ -40,22 +39,42 @@ pub fn expand_id_zst(input: ItemStruct) -> syn::Result<TokenStream> {
)?;
let extra_impls = if let Some(validate) = meta.validate {
expand_checked_impls(id, &owned, validate)
expand_checked_impls(&input, validate)
} else {
expand_unchecked_impls(id, &owned)
assert!(
input.generics.params.is_empty(),
"generic unchecked IDs are not currently supported"
);
expand_unchecked_impls(&input)
};
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
// So we don't have to insert #where_clause everywhere when it is always None in practice
assert_eq!(where_clause, None, "where clauses on identifier types are not currently supported");
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);
let as_str_impl = match &input.fields {
Fields::Named(_) | Fields::Unit => {
syn::Error::new(Span::call_site(), "Only tuple structs are supported currently.")
.into_compile_error()
}
Fields::Unnamed(u) => {
let last_idx = Index::from(u.unnamed.len() - 1);
quote! { &self.#last_idx }
}
};
let partial_eq_string = expand_partial_eq_string(quote! { #id #ty_generics }, &impl_generics);
// FIXME: Remove?
let box_partial_eq_string = expand_partial_eq_string(quote! { Box<#id> });
let box_partial_eq_string =
expand_partial_eq_string(quote! { Box<#id #ty_generics> }, &impl_generics);
Ok(quote! {
#owned_decl
impl #id {
impl #impl_generics #id #ty_generics {
pub(super) fn from_borrowed(s: &str) -> &Self {
unsafe { std::mem::transmute(s) }
}
@ -77,111 +96,113 @@ pub fn expand_id_zst(input: ItemStruct) -> syn::Result<TokenStream> {
}
#[doc = #as_str_docs]
#[inline]
pub fn as_str(&self) -> &str {
&self.0
#as_str_impl
}
#[doc = #as_bytes_docs]
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
self.as_str().as_bytes()
}
}
impl Clone for Box<#id> {
impl #impl_generics Clone for Box<#id #ty_generics> {
fn clone(&self) -> Self {
(**self).into()
}
}
impl ToOwned for #id {
type Owned = Box<#id>;
impl #impl_generics ToOwned for #id #ty_generics {
type Owned = Box<#id #ty_generics>;
fn to_owned(&self) -> Self::Owned {
Self::from_box(self.0.into())
Self::from_box(self.as_str().into())
}
}
impl AsRef<str> for #id {
impl #impl_generics AsRef<str> for #id #ty_generics {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<str> for Box<#id> {
impl #impl_generics AsRef<str> for Box<#id #ty_generics> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl From<&#id> for String {
fn from(id: &#id) -> Self {
impl #impl_generics From<&#id #ty_generics> for String {
fn from(id: &#id #ty_generics) -> Self {
id.as_str().to_owned()
}
}
impl From<Box<#id>> for String {
fn from(id: Box<#id>) -> Self {
impl #impl_generics From<Box<#id #ty_generics>> for String {
fn from(id: Box<#id #ty_generics>) -> Self {
id.into_owned().into()
}
}
impl From<&#id> for Box<#id> {
fn from(id: &#id) -> Self {
#id::from_box(id.0.into())
impl #impl_generics From<&#id #ty_generics> for Box<#id #ty_generics> {
fn from(id: &#id #ty_generics) -> Self {
<#id #ty_generics>::from_box(id.as_str().into())
}
}
impl From<&#id> for std::rc::Rc<#id> {
fn from(s: &#id) -> std::rc::Rc<#id> {
impl #impl_generics From<&#id #ty_generics> for std::rc::Rc<#id #ty_generics> {
fn from(s: &#id #ty_generics) -> std::rc::Rc<#id #ty_generics> {
let rc = std::rc::Rc::<str>::from(s.as_str());
<#id>::from_rc(rc)
<#id #ty_generics>::from_rc(rc)
}
}
impl From<&#id> for std::sync::Arc<#id> {
fn from(s: &#id) -> std::sync::Arc<#id> {
impl #impl_generics From<&#id #ty_generics> for std::sync::Arc<#id #ty_generics> {
fn from(s: &#id #ty_generics) -> std::sync::Arc<#id #ty_generics> {
let arc = std::sync::Arc::<str>::from(s.as_str());
<#id>::from_arc(arc)
<#id #ty_generics>::from_arc(arc)
}
}
impl PartialEq<#id> for Box<#id> {
fn eq(&self, other: &#id) -> bool {
impl #impl_generics PartialEq<#id #ty_generics> for Box<#id #ty_generics> {
fn eq(&self, other: &#id #ty_generics) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialEq<&'_ #id> for Box<#id> {
fn eq(&self, other: &&#id) -> bool {
impl #impl_generics PartialEq<&'_ #id #ty_generics> for Box<#id #ty_generics> {
fn eq(&self, other: &&#id #ty_generics) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialEq<Box<#id>> for #id {
fn eq(&self, other: &Box<#id>) -> bool {
impl #impl_generics PartialEq<Box<#id #ty_generics>> for #id #ty_generics {
fn eq(&self, other: &Box<#id #ty_generics>) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialEq<Box<#id>> for &'_ #id {
fn eq(&self, other: &Box<#id>) -> bool {
impl #impl_generics PartialEq<Box<#id #ty_generics>> for &'_ #id #ty_generics {
fn eq(&self, other: &Box<#id #ty_generics>) -> bool {
self.as_str() == other.as_str()
}
}
impl std::fmt::Debug for #id {
impl #impl_generics std::fmt::Debug for #id #ty_generics {
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 {
impl #impl_generics std::fmt::Display for #id #ty_generics {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl serde::Serialize for #id {
impl #impl_generics serde::Serialize for #id #ty_generics {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
@ -196,9 +217,14 @@ pub fn expand_id_zst(input: ItemStruct) -> syn::Result<TokenStream> {
})
}
fn expand_owned_id(id: &Ident, owned: &Ident) -> TokenStream {
fn expand_owned_id(input: &ItemStruct) -> TokenStream {
let id = &input.ident;
let owned = format_ident!("Owned{}", id);
let doc_header = format!("Owned variant of {}", id);
let partial_eq_string = expand_partial_eq_string(owned);
let (impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl();
let partial_eq_string =
expand_partial_eq_string(quote! { #owned #ty_generics }, &impl_generics);
quote! {
#[doc = #doc_header]
@ -209,53 +235,53 @@ fn expand_owned_id(id: &Ident, owned: &Ident) -> TokenStream {
/// 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 {
pub struct #owned #impl_generics {
#[cfg(not(any(ruma_identifiers_storage = "Arc")))]
inner: Box<#id>,
inner: Box<#id #ty_generics>,
#[cfg(ruma_identifiers_storage = "Arc")]
inner: std::sync::Arc<#id>,
inner: std::sync::Arc<#id #ty_generics>,
}
impl AsRef<#id> for #owned {
fn as_ref(&self) -> &#id {
impl #impl_generics AsRef<#id #ty_generics> for #owned #ty_generics {
fn as_ref(&self) -> &#id #ty_generics {
&*self.inner
}
}
impl AsRef<str> for #owned {
impl #impl_generics AsRef<str> for #owned #ty_generics {
fn as_ref(&self) -> &str {
(*self.inner).as_ref()
}
}
impl std::ops::Deref for #owned {
type Target = #id;
impl #impl_generics std::ops::Deref for #owned #ty_generics {
type Target = #id #ty_generics;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl std::borrow::Borrow<#id> for #owned {
fn borrow(&self) -> &#id {
impl #impl_generics std::borrow::Borrow<#id #ty_generics> for #owned #ty_generics {
fn borrow(&self) -> &#id #ty_generics {
self.as_ref()
}
}
impl From<&'_ #id> for #owned {
fn from(id: &#id) -> #owned {
impl #impl_generics From<&'_ #id #ty_generics> for #owned #ty_generics {
fn from(id: &#id #ty_generics) -> #owned #ty_generics {
#owned { inner: id.into() }
}
}
impl From<Box<#id>> for #owned {
fn from(b: Box<#id>) -> #owned {
impl #impl_generics From<Box<#id #ty_generics>> for #owned #ty_generics {
fn from(b: Box<#id #ty_generics>) -> #owned #ty_generics {
Self { inner: b.into() }
}
}
impl From<std::sync::Arc<#id>> for #owned {
fn from(a: std::sync::Arc<#id>) -> #owned {
impl #impl_generics From<std::sync::Arc<#id #ty_generics>> for #owned #ty_generics {
fn from(a: std::sync::Arc<#id #ty_generics>) -> #owned #ty_generics {
Self {
#[cfg(not(any(ruma_identifiers_storage = "Arc")))]
inner: a.as_ref().into(),
@ -265,13 +291,13 @@ fn expand_owned_id(id: &Ident, owned: &Ident) -> TokenStream {
}
}
impl std::fmt::Display for #owned {
impl #impl_generics std::fmt::Display for #owned #ty_generics {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl serde::Serialize for #owned {
impl #impl_generics serde::Serialize for #owned #ty_generics {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
@ -282,51 +308,57 @@ fn expand_owned_id(id: &Ident, owned: &Ident) -> TokenStream {
#partial_eq_string
impl PartialEq<#id> for #owned {
fn eq(&self, other: &#id) -> bool {
AsRef::<#id>::as_ref(self) == other
impl #impl_generics PartialEq<#id #ty_generics> for #owned #ty_generics {
fn eq(&self, other: &#id #ty_generics) -> bool {
AsRef::<#id #ty_generics>::as_ref(self) == other
}
}
impl PartialEq<#owned> for #id {
fn eq(&self, other: &#owned) -> bool {
self == AsRef::<#id>::as_ref(other)
impl #impl_generics PartialEq<#owned #ty_generics> for #id #ty_generics {
fn eq(&self, other: &#owned #ty_generics) -> bool {
self == AsRef::<#id #ty_generics>::as_ref(other)
}
}
impl PartialEq<&#id> for #owned {
fn eq(&self, other: &&#id) -> bool {
AsRef::<#id>::as_ref(self) == *other
impl #impl_generics PartialEq<&#id #ty_generics> for #owned #ty_generics {
fn eq(&self, other: &&#id #ty_generics) -> bool {
AsRef::<#id #ty_generics>::as_ref(self) == *other
}
}
impl PartialEq<#owned> for &#id {
fn eq(&self, other: &#owned) -> bool {
*self == AsRef::<#id>::as_ref(other)
impl #impl_generics PartialEq<#owned #ty_generics> for &#id #ty_generics {
fn eq(&self, other: &#owned #ty_generics) -> bool {
*self == AsRef::<#id #ty_generics>::as_ref(other)
}
}
impl PartialEq<Box<#id>> for #owned {
fn eq(&self, other: &Box<#id>) -> bool {
AsRef::<#id>::as_ref(self) == AsRef::<#id>::as_ref(other)
impl #impl_generics PartialEq<Box<#id #ty_generics>> for #owned #ty_generics {
fn eq(&self, other: &Box<#id #ty_generics>) -> bool {
AsRef::<#id #ty_generics>::as_ref(self) == AsRef::<#id #ty_generics>::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)
impl #impl_generics PartialEq<#owned #ty_generics> for Box<#id #ty_generics> {
fn eq(&self, other: &#owned #ty_generics) -> bool {
AsRef::<#id #ty_generics>::as_ref(self) == AsRef::<#id #ty_generics>::as_ref(other)
}
}
}
}
fn expand_checked_impls(id: &Ident, owned: &Ident, validate: Path) -> TokenStream {
fn expand_checked_impls(input: &ItemStruct, validate: Path) -> TokenStream {
let id = &input.ident;
let owned = format_ident!("Owned{}", id);
let (impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl();
let generic_params = &input.generics.params;
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 {
impl #impl_generics #id #ty_generics {
#[doc = #parse_doc_header]
///
/// The same can also be done using `FromStr`, `TryFrom` or `TryInto`.
@ -355,7 +387,7 @@ fn expand_checked_impls(id: &Ident, owned: &Ident, validate: Path) -> TokenStrea
}
}
impl<'de> serde::Deserialize<'de> for Box<#id> {
impl<'de, #generic_params> serde::Deserialize<'de> for Box<#id #ty_generics> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
@ -371,7 +403,7 @@ fn expand_checked_impls(id: &Ident, owned: &Ident, validate: Path) -> TokenStrea
}
}
impl<'de> serde::Deserialize<'de> for #owned {
impl<'de, #generic_params> serde::Deserialize<'de> for #owned #ty_generics {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
@ -387,42 +419,45 @@ fn expand_checked_impls(id: &Ident, owned: &Ident, validate: Path) -> TokenStrea
}
}
impl<'a> std::convert::TryFrom<&'a str> for &'a #id {
impl<'a, #generic_params> std::convert::TryFrom<&'a str> for &'a #id #ty_generics {
type Error = crate::IdParseError;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
#validate(s)?;
Ok(#id::from_borrowed(s))
Ok(<#id #ty_generics>::from_borrowed(s))
}
}
impl std::str::FromStr for Box<#id> {
impl #impl_generics std::str::FromStr for Box<#id #ty_generics> {
type Err = crate::IdParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#id::parse(s)
<#id #ty_generics>::parse(s)
}
}
impl std::convert::TryFrom<&str> for Box<#id> {
impl #impl_generics std::convert::TryFrom<&str> for Box<#id #ty_generics> {
type Error = crate::IdParseError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
#id::parse(s)
<#id #ty_generics>::parse(s)
}
}
impl std::convert::TryFrom<String> for Box<#id> {
impl #impl_generics std::convert::TryFrom<String> for Box<#id #ty_generics> {
type Error = crate::IdParseError;
fn try_from(s: String) -> Result<Self, Self::Error> {
#id::parse(s)
<#id #ty_generics>::parse(s)
}
}
}
}
fn expand_unchecked_impls(id: &Ident, owned: &Ident) -> TokenStream {
fn expand_unchecked_impls(input: &ItemStruct) -> TokenStream {
let id = &input.ident;
let owned = format_ident!("Owned{}", id);
quote! {
impl<'a> From<&'a str> for &'a #id {
fn from(s: &'a str) -> Self {
@ -475,28 +510,26 @@ fn expand_unchecked_impls(id: &Ident, owned: &Ident) -> TokenStream {
}
}
fn expand_partial_eq_string(id: impl ToTokens) -> TokenStream {
fn single_impl(lhs: impl ToTokens, rhs: impl ToTokens) -> TokenStream {
fn expand_partial_eq_string(ty: TokenStream, impl_generics: &ImplGenerics<'_>) -> TokenStream {
IntoIterator::into_iter([
(ty.clone(), quote! { str }),
(ty.clone(), quote! { &str }),
(ty.clone(), quote! { String }),
(quote! { str }, ty.clone()),
(quote! { &str }, ty.clone()),
(quote! { String }, ty),
])
.map(|(lhs, rhs)| {
quote! {
impl PartialEq<#rhs> for #lhs {
impl #impl_generics 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
})
.collect()
}
mod kw {