1use 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 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 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 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 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 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 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}