ruma_macros/
identifiers.rs

1//! Methods and types for generating identifiers.
2
3use proc_macro2::{Span, TokenStream};
4use quote::{format_ident, quote};
5use syn::{
6    parse::{Parse, ParseStream},
7    punctuated::Punctuated,
8    Fields, ImplGenerics, Index, ItemStruct, LitStr, Path, Token,
9};
10
11pub struct IdentifierInput {
12    pub dollar_crate: Path,
13    pub id: LitStr,
14}
15
16impl Parse for IdentifierInput {
17    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
18        let dollar_crate = input.parse()?;
19        let _: Token![,] = input.parse()?;
20        let id = input.parse()?;
21
22        Ok(Self { dollar_crate, id })
23    }
24}
25
26pub fn expand_id_zst(input: ItemStruct) -> syn::Result<TokenStream> {
27    let id = &input.ident;
28    let owned = format_ident!("Owned{id}");
29
30    let owned_decl = expand_owned_id(&input);
31
32    let meta = input.attrs.iter().filter(|attr| attr.path().is_ident("ruma_id")).try_fold(
33        IdZstMeta::default(),
34        |meta, attr| {
35            let list: Punctuated<IdZstMeta, Token![,]> =
36                attr.parse_args_with(Punctuated::parse_terminated)?;
37
38            list.into_iter().try_fold(meta, IdZstMeta::merge)
39        },
40    )?;
41
42    let extra_impls = if let Some(validate) = meta.validate {
43        expand_checked_impls(&input, validate)
44    } else {
45        assert!(
46            input.generics.params.is_empty(),
47            "generic unchecked IDs are not currently supported"
48        );
49        expand_unchecked_impls(&input)
50    };
51
52    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
53    // So we don't have to insert #where_clause everywhere when it is always None in practice
54    assert_eq!(where_clause, None, "where clauses on identifier types are not currently supported");
55
56    let as_str_docs = format!("Creates a string slice from this `{id}`.");
57    let as_bytes_docs = format!("Creates a byte slice from this `{id}`.");
58
59    let as_str_impl = match &input.fields {
60        Fields::Named(_) | Fields::Unit => {
61            syn::Error::new(Span::call_site(), "Only tuple structs are supported currently.")
62                .into_compile_error()
63        }
64        Fields::Unnamed(u) => {
65            let last_idx = Index::from(u.unnamed.len() - 1);
66            quote! { &self.#last_idx }
67        }
68    };
69
70    let id_ty = quote! { #id #ty_generics };
71    let owned_ty = quote! { #owned #ty_generics };
72
73    let as_str_impls = expand_as_str_impls(id_ty.clone(), &impl_generics);
74    // FIXME: Remove?
75    let box_partial_eq_string = expand_partial_eq_string(quote! { Box<#id_ty> }, &impl_generics);
76
77    Ok(quote! {
78        #owned_decl
79
80        #[automatically_derived]
81        impl #impl_generics #id_ty {
82            pub(super) const fn from_borrowed(s: &str) -> &Self {
83                unsafe { std::mem::transmute(s) }
84            }
85
86            pub(super) fn from_box(s: Box<str>) -> Box<Self> {
87                unsafe { Box::from_raw(Box::into_raw(s) as _) }
88            }
89
90            pub(super) fn from_rc(s: std::rc::Rc<str>) -> std::rc::Rc<Self> {
91                unsafe { std::rc::Rc::from_raw(std::rc::Rc::into_raw(s) as _) }
92            }
93
94            pub(super) fn from_arc(s: std::sync::Arc<str>) -> std::sync::Arc<Self> {
95                unsafe { std::sync::Arc::from_raw(std::sync::Arc::into_raw(s) as _) }
96            }
97
98            pub(super) fn into_owned(self: Box<Self>) -> Box<str> {
99                unsafe { Box::from_raw(Box::into_raw(self) as _) }
100            }
101
102            #[doc = #as_str_docs]
103            #[inline]
104            pub fn as_str(&self) -> &str {
105                #as_str_impl
106            }
107
108            #[doc = #as_bytes_docs]
109            #[inline]
110            pub fn as_bytes(&self) -> &[u8] {
111                self.as_str().as_bytes()
112            }
113        }
114
115        #[automatically_derived]
116        impl #impl_generics Clone for Box<#id_ty> {
117            fn clone(&self) -> Self {
118                (**self).into()
119            }
120        }
121
122        #[automatically_derived]
123        impl #impl_generics ToOwned for #id_ty {
124            type Owned = #owned_ty;
125
126            fn to_owned(&self) -> Self::Owned {
127                #owned::from_ref(self)
128            }
129        }
130
131        #[automatically_derived]
132        impl #impl_generics AsRef<#id_ty> for #id_ty {
133            fn as_ref(&self) -> &#id_ty {
134                self
135            }
136        }
137
138        #[automatically_derived]
139        impl #impl_generics AsRef<str> for #id_ty {
140            fn as_ref(&self) -> &str {
141                self.as_str()
142            }
143        }
144
145        #[automatically_derived]
146        impl #impl_generics AsRef<str> for Box<#id_ty> {
147            fn as_ref(&self) -> &str {
148                self.as_str()
149            }
150        }
151
152        #[automatically_derived]
153        impl #impl_generics AsRef<[u8]> for #id_ty {
154            fn as_ref(&self) -> &[u8] {
155                self.as_bytes()
156            }
157        }
158
159        #[automatically_derived]
160        impl #impl_generics AsRef<[u8]> for Box<#id_ty> {
161            fn as_ref(&self) -> &[u8] {
162                self.as_bytes()
163            }
164        }
165
166        #[automatically_derived]
167        impl #impl_generics From<&#id_ty> for String {
168            fn from(id: &#id_ty) -> Self {
169                id.as_str().to_owned()
170            }
171        }
172
173        #[automatically_derived]
174        impl #impl_generics From<Box<#id_ty>> for String {
175            fn from(id: Box<#id_ty>) -> Self {
176                id.into_owned().into()
177            }
178        }
179
180        #[automatically_derived]
181        impl #impl_generics From<&#id_ty> for Box<#id_ty> {
182            fn from(id: &#id_ty) -> Self {
183                <#id_ty>::from_box(id.as_str().into())
184            }
185        }
186
187        #[automatically_derived]
188        impl #impl_generics From<&#id_ty> for std::rc::Rc<#id_ty> {
189            fn from(s: &#id_ty) -> std::rc::Rc<#id_ty> {
190                let rc = std::rc::Rc::<str>::from(s.as_str());
191                <#id_ty>::from_rc(rc)
192            }
193        }
194
195        #[automatically_derived]
196        impl #impl_generics From<&#id_ty> for std::sync::Arc<#id_ty> {
197            fn from(s: &#id_ty) -> std::sync::Arc<#id_ty> {
198                let arc = std::sync::Arc::<str>::from(s.as_str());
199                <#id_ty>::from_arc(arc)
200            }
201        }
202
203        #[automatically_derived]
204        impl #impl_generics PartialEq<#id_ty> for Box<#id_ty> {
205            fn eq(&self, other: &#id_ty) -> bool {
206                self.as_str() == other.as_str()
207            }
208        }
209
210        #[automatically_derived]
211        impl #impl_generics PartialEq<&'_ #id_ty> for Box<#id_ty> {
212            fn eq(&self, other: &&#id_ty) -> bool {
213                self.as_str() == other.as_str()
214            }
215        }
216
217        #[automatically_derived]
218        impl #impl_generics PartialEq<Box<#id_ty>> for #id_ty {
219            fn eq(&self, other: &Box<#id_ty>) -> bool {
220                self.as_str() == other.as_str()
221            }
222        }
223
224        #[automatically_derived]
225        impl #impl_generics PartialEq<Box<#id_ty>> for &'_ #id_ty {
226            fn eq(&self, other: &Box<#id_ty>) -> bool {
227                self.as_str() == other.as_str()
228            }
229        }
230
231        #as_str_impls
232        #box_partial_eq_string
233        #extra_impls
234    })
235}
236
237fn expand_owned_id(input: &ItemStruct) -> TokenStream {
238    let id = &input.ident;
239    let owned = format_ident!("Owned{id}");
240
241    let doc_header = format!("Owned variant of {id}");
242    let (impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl();
243
244    let id_ty = quote! { #id #ty_generics };
245    let owned_ty = quote! { #owned #ty_generics };
246
247    let as_str_impls = expand_as_str_impls(owned_ty.clone(), &impl_generics);
248
249    quote! {
250        #[doc = #doc_header]
251        ///
252        /// The wrapper type for this type is variable, by default it'll use [`Box`],
253        /// but you can change that by setting "`--cfg=ruma_identifiers_storage=...`" using
254        /// `RUSTFLAGS` or `.cargo/config.toml` (under `[build]` -> `rustflags = ["..."]`)
255        /// to the following;
256        /// - `ruma_identifiers_storage="Arc"` to use [`Arc`](std::sync::Arc) as a wrapper type.
257        pub struct #owned #impl_generics {
258            #[cfg(not(any(ruma_identifiers_storage = "Arc")))]
259            inner: Box<#id_ty>,
260            #[cfg(ruma_identifiers_storage = "Arc")]
261            inner: std::sync::Arc<#id_ty>,
262        }
263
264        #[automatically_derived]
265        impl #impl_generics #owned_ty {
266            fn from_ref(v: &#id_ty) -> Self {
267                Self {
268                    #[cfg(not(any(ruma_identifiers_storage = "Arc")))]
269                    inner: #id::from_box(v.as_str().into()),
270                    #[cfg(ruma_identifiers_storage = "Arc")]
271                    inner: #id::from_arc(v.as_str().into()),
272                }
273            }
274        }
275
276        #[automatically_derived]
277        impl #impl_generics AsRef<#id_ty> for #owned_ty {
278            fn as_ref(&self) -> &#id_ty {
279                &*self.inner
280            }
281        }
282
283        #[automatically_derived]
284        impl #impl_generics AsRef<str> for #owned_ty {
285            fn as_ref(&self) -> &str {
286                self.inner.as_str()
287            }
288        }
289
290        #[automatically_derived]
291        impl #impl_generics AsRef<[u8]> for #owned_ty {
292            fn as_ref(&self) -> &[u8] {
293                self.inner.as_bytes()
294            }
295        }
296
297        #[automatically_derived]
298        impl #impl_generics From<#owned_ty> for String {
299            fn from(id: #owned_ty) -> String {
300                #[cfg(not(any(ruma_identifiers_storage = "Arc")))]
301                { id.inner.into() }
302                #[cfg(ruma_identifiers_storage = "Arc")]
303                { id.inner.as_ref().into() }
304            }
305        }
306
307        #[automatically_derived]
308        impl #impl_generics std::clone::Clone for #owned_ty {
309            fn clone(&self) -> Self {
310                (&*self.inner).into()
311            }
312        }
313
314        #[automatically_derived]
315        impl #impl_generics std::ops::Deref for #owned_ty {
316            type Target = #id_ty;
317
318            fn deref(&self) -> &Self::Target {
319                &self.inner
320            }
321        }
322
323        #[automatically_derived]
324        impl #impl_generics std::borrow::Borrow<#id_ty> for #owned_ty {
325            fn borrow(&self) -> &#id_ty {
326                self.as_ref()
327            }
328        }
329
330        #[automatically_derived]
331        impl #impl_generics From<&'_ #id_ty> for #owned_ty {
332            fn from(id: &#id_ty) -> #owned_ty {
333                #owned { inner: id.into() }
334            }
335        }
336
337        #[automatically_derived]
338        impl #impl_generics From<Box<#id_ty>> for #owned_ty {
339            fn from(b: Box<#id_ty>) -> #owned_ty {
340                Self { inner: b.into() }
341            }
342        }
343
344        #[automatically_derived]
345        impl #impl_generics From<std::sync::Arc<#id_ty>> for #owned_ty {
346            fn from(a: std::sync::Arc<#id_ty>) -> #owned_ty {
347                Self {
348                    #[cfg(not(any(ruma_identifiers_storage = "Arc")))]
349                    inner: a.as_ref().into(),
350                    #[cfg(ruma_identifiers_storage = "Arc")]
351                    inner: a,
352                }
353            }
354        }
355
356        #[automatically_derived]
357        impl #impl_generics From<#owned_ty> for Box<#id_ty> {
358            fn from(a: #owned_ty) -> Box<#id_ty> {
359                #[cfg(not(any(ruma_identifiers_storage = "Arc")))]
360                { a.inner }
361                #[cfg(ruma_identifiers_storage = "Arc")]
362                { a.inner.as_ref().into() }
363            }
364        }
365
366        #[automatically_derived]
367        impl #impl_generics From<#owned_ty> for std::sync::Arc<#id_ty> {
368            fn from(a: #owned_ty) -> std::sync::Arc<#id_ty> {
369                #[cfg(not(any(ruma_identifiers_storage = "Arc")))]
370                { a.inner.into() }
371                #[cfg(ruma_identifiers_storage = "Arc")]
372                { a.inner }
373            }
374        }
375
376        #[automatically_derived]
377        impl #impl_generics std::cmp::PartialEq for #owned_ty {
378            fn eq(&self, other: &Self) -> bool {
379                self.as_str() == other.as_str()
380            }
381        }
382
383        #[automatically_derived]
384        impl #impl_generics std::cmp::Eq for #owned_ty {}
385
386        #[automatically_derived]
387        impl #impl_generics std::cmp::PartialOrd for #owned_ty {
388            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
389                Some(self.cmp(other))
390            }
391        }
392
393        #[automatically_derived]
394        impl #impl_generics std::cmp::Ord for #owned_ty {
395            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
396                self.as_str().cmp(other.as_str())
397            }
398        }
399
400        #[automatically_derived]
401        impl #impl_generics std::hash::Hash for #owned_ty {
402            fn hash<H>(&self, state: &mut H)
403            where
404                H: std::hash::Hasher,
405            {
406                self.as_str().hash(state)
407            }
408        }
409
410        #as_str_impls
411
412        #[automatically_derived]
413        impl #impl_generics PartialEq<#id_ty> for #owned_ty {
414            fn eq(&self, other: &#id_ty) -> bool {
415                AsRef::<#id_ty>::as_ref(self) == other
416            }
417        }
418
419        #[automatically_derived]
420        impl #impl_generics PartialEq<#owned_ty> for #id_ty {
421            fn eq(&self, other: &#owned_ty) -> bool {
422                self == AsRef::<#id_ty>::as_ref(other)
423            }
424        }
425
426        #[automatically_derived]
427        impl #impl_generics PartialEq<&#id_ty> for #owned_ty {
428            fn eq(&self, other: &&#id_ty) -> bool {
429                AsRef::<#id_ty>::as_ref(self) == *other
430            }
431        }
432
433        #[automatically_derived]
434        impl #impl_generics PartialEq<#owned_ty> for &#id_ty {
435            fn eq(&self, other: &#owned_ty) -> bool {
436                *self == AsRef::<#id_ty>::as_ref(other)
437            }
438        }
439
440        #[automatically_derived]
441        impl #impl_generics PartialEq<Box<#id_ty>> for #owned_ty {
442            fn eq(&self, other: &Box<#id_ty>) -> bool {
443                AsRef::<#id_ty>::as_ref(self) == AsRef::<#id_ty>::as_ref(other)
444            }
445        }
446
447        #[automatically_derived]
448        impl #impl_generics PartialEq<#owned_ty> for Box<#id_ty> {
449            fn eq(&self, other: &#owned_ty) -> bool {
450                AsRef::<#id_ty>::as_ref(self) == AsRef::<#id_ty>::as_ref(other)
451            }
452        }
453
454        #[automatically_derived]
455        impl #impl_generics PartialEq<std::sync::Arc<#id_ty>> for #owned_ty {
456            fn eq(&self, other: &std::sync::Arc<#id_ty>) -> bool {
457                AsRef::<#id_ty>::as_ref(self) == AsRef::<#id_ty>::as_ref(other)
458            }
459        }
460
461        #[automatically_derived]
462        impl #impl_generics PartialEq<#owned_ty> for std::sync::Arc<#id_ty> {
463            fn eq(&self, other: &#owned_ty) -> bool {
464                AsRef::<#id_ty>::as_ref(self) == AsRef::<#id_ty>::as_ref(other)
465            }
466        }
467    }
468}
469
470fn expand_checked_impls(input: &ItemStruct, validate: Path) -> TokenStream {
471    let id = &input.ident;
472    let owned = format_ident!("Owned{id}");
473
474    let (impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl();
475    let generic_params = &input.generics.params;
476
477    let parse_doc_header = format!("Try parsing a `&str` into an `Owned{id}`.");
478    let parse_box_doc_header = format!("Try parsing a `&str` into a `Box<{id}>`.");
479    let parse_rc_docs = format!("Try parsing a `&str` into an `Rc<{id}>`.");
480    let parse_arc_docs = format!("Try parsing a `&str` into an `Arc<{id}>`.");
481
482    let id_ty = quote! { #id #ty_generics };
483    let owned_ty = quote! { #owned #ty_generics };
484
485    quote! {
486        #[automatically_derived]
487        impl #impl_generics #id_ty {
488            #[doc = #parse_doc_header]
489            ///
490            /// The same can also be done using `FromStr`, `TryFrom` or `TryInto`.
491            /// This function is simply more constrained and thus useful in generic contexts.
492            pub fn parse(
493                s: impl AsRef<str>,
494            ) -> Result<#owned_ty, crate::IdParseError> {
495                let s = s.as_ref();
496                #validate(s)?;
497                Ok(#id::from_borrowed(s).to_owned())
498            }
499
500            #[doc = #parse_box_doc_header]
501            ///
502            /// The same can also be done using `FromStr`, `TryFrom` or `TryInto`.
503            /// This function is simply more constrained and thus useful in generic contexts.
504            pub fn parse_box(
505                s: impl AsRef<str> + Into<Box<str>>,
506            ) -> Result<Box<Self>, crate::IdParseError> {
507                #validate(s.as_ref())?;
508                Ok(#id::from_box(s.into()))
509            }
510
511            #[doc = #parse_rc_docs]
512            pub fn parse_rc(
513                s: impl AsRef<str> + Into<std::rc::Rc<str>>,
514            ) -> Result<std::rc::Rc<Self>, crate::IdParseError> {
515                #validate(s.as_ref())?;
516                Ok(#id::from_rc(s.into()))
517            }
518
519            #[doc = #parse_arc_docs]
520            pub fn parse_arc(
521                s: impl AsRef<str> + Into<std::sync::Arc<str>>,
522            ) -> Result<std::sync::Arc<Self>, crate::IdParseError> {
523                #validate(s.as_ref())?;
524                Ok(#id::from_arc(s.into()))
525            }
526        }
527
528        #[automatically_derived]
529        impl<'de, #generic_params> serde::Deserialize<'de> for Box<#id_ty> {
530            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
531            where
532                D: serde::Deserializer<'de>,
533            {
534                use serde::de::Error;
535
536                let s = String::deserialize(deserializer)?;
537
538                match #id::parse_box(s) {
539                    Ok(o) => Ok(o),
540                    Err(e) => Err(D::Error::custom(e)),
541                }
542            }
543        }
544
545        #[automatically_derived]
546        impl<'de, #generic_params> serde::Deserialize<'de> for #owned_ty {
547            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
548            where
549                D: serde::Deserializer<'de>,
550            {
551                use serde::de::Error;
552
553                let s = String::deserialize(deserializer)?;
554
555                match #id::parse(s) {
556                    Ok(o) => Ok(o),
557                    Err(e) => Err(D::Error::custom(e)),
558                }
559            }
560        }
561
562        #[automatically_derived]
563        impl<'a, #generic_params> std::convert::TryFrom<&'a str> for &'a #id_ty {
564            type Error = crate::IdParseError;
565
566            fn try_from(s: &'a str) -> Result<Self, Self::Error> {
567                #validate(s)?;
568                Ok(<#id_ty>::from_borrowed(s))
569            }
570        }
571
572        #[automatically_derived]
573        impl #impl_generics std::str::FromStr for Box<#id_ty> {
574            type Err = crate::IdParseError;
575
576            fn from_str(s: &str) -> Result<Self, Self::Err> {
577                <#id_ty>::parse_box(s)
578            }
579        }
580
581        #[automatically_derived]
582        impl #impl_generics std::convert::TryFrom<&str> for Box<#id_ty> {
583            type Error = crate::IdParseError;
584
585            fn try_from(s: &str) -> Result<Self, Self::Error> {
586                <#id_ty>::parse_box(s)
587            }
588        }
589
590        #[automatically_derived]
591        impl #impl_generics std::convert::TryFrom<String> for Box<#id_ty> {
592            type Error = crate::IdParseError;
593
594            fn try_from(s: String) -> Result<Self, Self::Error> {
595                <#id_ty>::parse_box(s)
596            }
597        }
598
599        #[automatically_derived]
600        impl #impl_generics std::str::FromStr for #owned_ty {
601            type Err = crate::IdParseError;
602
603            fn from_str(s: &str) -> Result<Self, Self::Err> {
604                <#id_ty>::parse(s)
605            }
606        }
607
608        #[automatically_derived]
609        impl #impl_generics std::convert::TryFrom<&str> for #owned_ty {
610            type Error = crate::IdParseError;
611
612            fn try_from(s: &str) -> Result<Self, Self::Error> {
613                <#id_ty>::parse(s)
614            }
615        }
616
617        #[automatically_derived]
618        impl #impl_generics std::convert::TryFrom<String> for #owned_ty {
619            type Error = crate::IdParseError;
620
621            fn try_from(s: String) -> Result<Self, Self::Error> {
622                <#id_ty>::parse(s)
623            }
624        }
625    }
626}
627
628fn expand_unchecked_impls(input: &ItemStruct) -> TokenStream {
629    let id = &input.ident;
630    let owned = format_ident!("Owned{id}");
631
632    quote! {
633        #[automatically_derived]
634        impl<'a> From<&'a str> for &'a #id {
635            fn from(s: &'a str) -> Self {
636                #id::from_borrowed(s)
637            }
638        }
639
640        #[automatically_derived]
641        impl From<&str> for #owned {
642            fn from(s: &str) -> Self {
643                <&#id>::from(s).into()
644            }
645        }
646
647        #[automatically_derived]
648        impl From<Box<str>> for #owned {
649            fn from(s: Box<str>) -> Self {
650                <&#id>::from(&*s).into()
651            }
652        }
653
654        #[automatically_derived]
655        impl From<String> for #owned {
656            fn from(s: String) -> Self {
657                <&#id>::from(s.as_str()).into()
658            }
659        }
660
661        #[automatically_derived]
662        impl From<&str> for Box<#id> {
663            fn from(s: &str) -> Self {
664                #id::from_box(s.into())
665            }
666        }
667
668        #[automatically_derived]
669        impl From<Box<str>> for Box<#id> {
670            fn from(s: Box<str>) -> Self {
671                #id::from_box(s)
672            }
673        }
674
675        #[automatically_derived]
676        impl From<String> for Box<#id> {
677            fn from(s: String) -> Self {
678                #id::from_box(s.into())
679            }
680        }
681
682        #[automatically_derived]
683        impl From<Box<#id>> for Box<str> {
684            fn from(id: Box<#id>) -> Self {
685                id.into_owned()
686            }
687        }
688
689        #[automatically_derived]
690        impl<'de> serde::Deserialize<'de> for Box<#id> {
691            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
692            where
693                D: serde::Deserializer<'de>,
694            {
695                Box::<str>::deserialize(deserializer).map(#id::from_box)
696            }
697        }
698
699        #[automatically_derived]
700        impl<'de> serde::Deserialize<'de> for #owned {
701            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
702            where
703                D: serde::Deserializer<'de>,
704            {
705                // FIXME: Deserialize inner, convert that
706                Box::<str>::deserialize(deserializer).map(#id::from_box).map(Into::into)
707            }
708        }
709    }
710}
711
712fn expand_as_str_impls(ty: TokenStream, impl_generics: &ImplGenerics<'_>) -> TokenStream {
713    let partial_eq_string = expand_partial_eq_string(ty.clone(), impl_generics);
714
715    quote! {
716        #[automatically_derived]
717        impl #impl_generics std::fmt::Display for #ty {
718            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
719                write!(f, "{}", self.as_str())
720            }
721        }
722
723        #[automatically_derived]
724        impl #impl_generics std::fmt::Debug for #ty {
725            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
726                <str as std::fmt::Debug>::fmt(self.as_str(), f)
727            }
728        }
729
730        #[automatically_derived]
731        impl #impl_generics serde::Serialize for #ty {
732            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
733            where
734                S: serde::Serializer,
735            {
736                serializer.serialize_str(self.as_str())
737            }
738        }
739
740        #partial_eq_string
741    }
742}
743
744fn expand_partial_eq_string(ty: TokenStream, impl_generics: &ImplGenerics<'_>) -> TokenStream {
745    IntoIterator::into_iter([
746        (ty.clone(), quote! { str }),
747        (ty.clone(), quote! { &str }),
748        (ty.clone(), quote! { String }),
749        (quote! { str }, ty.clone()),
750        (quote! { &str }, ty.clone()),
751        (quote! { String }, ty),
752    ])
753    .map(|(lhs, rhs)| {
754        quote! {
755            #[automatically_derived]
756            impl #impl_generics PartialEq<#rhs> for #lhs {
757                fn eq(&self, other: &#rhs) -> bool {
758                    AsRef::<str>::as_ref(self)
759                        == AsRef::<str>::as_ref(other)
760                }
761            }
762        }
763    })
764    .collect()
765}
766
767mod kw {
768    syn::custom_keyword!(validate);
769}
770
771#[derive(Default)]
772struct IdZstMeta {
773    validate: Option<Path>,
774}
775
776impl IdZstMeta {
777    fn merge(self, other: IdZstMeta) -> syn::Result<Self> {
778        let validate = match (self.validate, other.validate) {
779            (None, None) => None,
780            (Some(val), None) | (None, Some(val)) => Some(val),
781            (Some(a), Some(b)) => {
782                let mut error = syn::Error::new_spanned(b, "duplicate attribute argument");
783                error.combine(syn::Error::new_spanned(a, "note: first one here"));
784                return Err(error);
785            }
786        };
787
788        Ok(Self { validate })
789    }
790}
791
792impl Parse for IdZstMeta {
793    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
794        let _: kw::validate = input.parse()?;
795        let _: Token![=] = input.parse()?;
796        let validate = Some(input.parse()?);
797        Ok(Self { validate })
798    }
799}