attrs/lib.rs
1//! An ergonomic [`Parser`] for `#[attributes]`, built on parser combinators.
2//!
3//! ```
4//! # strum_lite::strum!( #[derive(PartialEq, Debug)] enum Casing { Kebab = "kebab-case", Snake = "snake_case" });
5//! # fn main() -> syn::Result<()> {
6//! # use syn::*;
7//! # use attrs::*;
8//! # use quote::ToTokens as _;
9//! let mut rename_all = None::<Casing>;
10//! let mut untagged = false;
11//! let mut deny_unknown_fields = false;
12//! let mut path_to_serde: Path = parse_quote!(::serde);
13//! let attrs: Vec<Attribute> = parse_quote! {
14//! #[serde(rename_all = "kebab-case", untagged)]
15//! #[serde(crate = "custom::path")]
16//! };
17//!
18//! Attrs::new()
19//! .once("rename_all", with::eq(set::from_str(&mut rename_all)))
20//! .once("untagged", set::flag(&mut untagged))
21//! .once("deny_unknown_fields", set::flag(&mut deny_unknown_fields))
22//! .once("crate", with::eq(on::parse_str(&mut path_to_serde)))
23//! .parse_attrs("serde", &attrs)?;
24//!
25//! assert_eq!(rename_all, Some(Casing::Kebab));
26//! assert!(untagged);
27//! assert!(!deny_unknown_fields); // not encountered, so not set
28//! assert_eq!(path_to_serde.to_token_stream().to_string(), "custom :: path");
29//! # Ok(()) }
30//! ```
31//!
32//! # Guide
33//!
34//! `#[attributes]` as they are used [in the Rust compiler](https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax)
35//! and [in the wild](https://serde.rs/attributes.html) tend to look like this:
36//!
37//! ```
38//! # const _: &str = stringify! {
39//! #[repr(align(128), C)]
40//! // ^^^^ ^^^^^ ^^^ ^
41//! // path key (val) bare key
42//!
43//! #[serde(rename_all = "kebab-case", untagged)]
44//! // ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^
45//! // path key = val bare key
46//! # };
47//! ```
48//!
49//! To use this library, create an [`Attrs`],
50//! and register different `key`s, each with a parsing function.
51//!
52//! This library provides many parsing functions, but there are four key kinds:
53//! - [`lit`](set::lit) takes a literal like `true` or `100` from the input.
54//! - [`from_str`](set::from_str) takes a `".."` string from the input,
55//! before trying to [`FromStr`] it into an object.
56//! - [`parse_str`](set::parse_str) takes a `".."` string from the input,
57//! before trying to [`syn::parse`] it into an object.
58//! - [`parse`](set::parse) directly tries to [`syn::parse`] the input.
59//!
60//! Every function takes an `&mut` reference to its destination,
61//! which will be filled in when the corresponding `key` is encountered.
62//! The [`on`] module acts on direct references,
63//! whereas the [`set`] module acts on [`Option`]s, filling them with [`Some`].
64//!
65//! The main ways to separate a key from its value are provided as combinators in the [`with`] module:
66//! - [`with::eq`] take an `=` from the input.
67//! - [`with::paren`] take a group `(..)` from the input.
68//!
69//! You may choose to accept a `key` [`once`](Attrs::once) or [`many`](Attrs::many) times,
70//! and you can, of course, write your own parsing functions for whatever syntax you have in mind.
71
72use core::{fmt, fmt::Display, mem, str::FromStr};
73use std::{
74 borrow::Cow,
75 collections::{BTreeMap, btree_map::Entry},
76 rc::Rc,
77 sync::Arc,
78};
79
80use proc_macro2::{Span, TokenStream};
81use syn::{
82 Attribute, Ident, LitBool, LitStr, Token,
83 ext::IdentExt as _,
84 parse::{Parse, ParseStream, Parser},
85};
86
87/// Ergonomic [`Parser`] for `#[attributes]`.
88///
89/// See [crate documentation](mod@self) for more.
90///
91/// ```
92/// # fn main() -> syn::Result<()> {
93/// # use attrs::*;
94/// let mut untagged = false;
95/// let mut krate = None::<syn::Path>;
96///
97/// let parseme: syn::Attribute = syn::parse_quote! {
98/// #[serde(untagged, crate = "path::to::serde")]
99/// };
100///
101/// parseme.parse_args_with(
102/// Attrs::new()
103/// .once("untagged", set::flag(&mut untagged))
104/// .once("crate", with::eq(set::parse_str(&mut krate)))
105/// )?;
106///
107/// assert!(krate.is_some() && untagged);
108/// # Ok(()) }
109/// ```
110#[derive(Default)]
111pub struct Attrs<'a> {
112 map: BTreeMap<Ident, Attr<'a>>,
113 #[expect(clippy::type_complexity)]
114 fallback: Option<Box<dyn 'a + FnMut(&Ident, ParseStream<'_>) -> syn::Result<()>>>,
115}
116impl fmt::Debug for Attrs<'_> {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 f.debug_struct("Attrs")
119 .field("map", &self.map)
120 .field(
121 "fallback",
122 &match self.fallback {
123 Some(_) => "Some(..)",
124 None => "None",
125 },
126 )
127 .finish()
128 }
129}
130
131impl<'a> Attrs<'a> {
132 /// Create a new empty parser.
133 pub fn new() -> Self {
134 Self::default()
135 }
136 /// Whether `key` already exists in this parser.
137 pub fn contains<Q>(&self, key: &Q) -> bool
138 where
139 Q: ?Sized,
140 Ident: PartialEq<Q>,
141 {
142 self.map.keys().any(|it| it == key)
143 }
144 /// Parse tokens following `key` using `f`, at most once.
145 ///
146 /// See [crate documentation](mod@self) for more.
147 ///
148 /// # Panics
149 /// - If `key` has already been registered.
150 /// - If `key` is an invalid ident.
151 #[track_caller]
152 pub fn once<K, F>(&mut self, key: K, f: F) -> &mut Self
153 where
154 K: UnwrapIdent,
155 F: 'a + FnOnce(ParseStream<'_>) -> syn::Result<()>,
156 {
157 self.insert(key, Attr::Once(Once::Some(Box::new(f))))
158 }
159 /// Parse tokens following `key` using `f`, potentially many times.
160 ///
161 /// See [crate documentation](mod@self) for more.
162 ///
163 /// # Panics
164 /// - If `key` has already been registered.
165 /// - If `key` is an invalid ident.
166 #[track_caller]
167 pub fn many<K, F>(&mut self, key: K, f: F) -> &mut Self
168 where
169 K: UnwrapIdent,
170 F: 'a + FnMut(ParseStream<'_>) -> syn::Result<()>,
171 {
172 self.insert(key, Attr::Many(Box::new(f)))
173 }
174 /// Parse unrecognised keys using `f`.
175 ///
176 /// ```
177 /// # fn main() -> syn::Result<()> {
178 /// # use attrs::*;
179 /// let mut krate = None::<syn::Path>;
180 ///
181 /// let parseme: syn::Attribute = syn::parse_quote! {
182 /// #[serde(crate = "path::to::serde")]
183 /// };
184 ///
185 /// parseme.parse_args_with(Attrs::new().fallback(|key, input| {
186 /// assert_eq!(key, "crate");
187 /// input.parse::<syn::Token![=]>()?;
188 /// krate = Some(input.parse::<syn::LitStr>()?.parse()?);
189 /// Ok(())
190 /// }))?;
191 ///
192 /// assert!(krate.is_some());
193 /// # Ok(()) }
194 /// ```
195 pub fn fallback<F>(&mut self, f: F) -> &mut Self
196 where
197 F: 'a + FnMut(&Ident, ParseStream<'_>) -> syn::Result<()>,
198 {
199 self.fallback = Some(Box::new(f));
200 self
201 }
202 /// If the key `alias` is encountered, call the parser for `key`.
203 ///
204 /// See [module documentation](mod@self) for more.
205 ///
206 /// # Panics
207 /// - If `alias` has already been registered.
208 /// - If `alias` is an invalid ident.
209 /// - If `key` has not been registered.
210 #[track_caller]
211 pub fn alias<A, K>(&mut self, alias: A, key: K) -> &mut Self
212 where
213 A: UnwrapIdent,
214 K: UnwrapIdent,
215 {
216 let key = key.unwrap_ident();
217 assert!(
218 self.contains(&key),
219 "`{key}` is not registered (aliases may only be registered after their destination)"
220 );
221 self.insert(alias, Attr::AliasFor(key.unwrap_ident()))
222 }
223 /// Parse all the [`Attribute`]s where their path is the given `path`.
224 ///
225 /// ```
226 /// # fn main() -> syn::Result<()> {
227 /// # use syn::*;
228 /// # use attrs::*;
229 ///
230 /// let mut rename_all = None::<String>;
231 /// let mut untagged = false;
232 /// let mut deny_unknown_fields = false;
233 /// let attrs: Vec<Attribute> = parse_quote! {
234 /// #[serde(rename_all = "kebab-case", untagged)]
235 /// #[default] // SKIPPED
236 /// #[serde(deny_unknown_fields)]
237 /// };
238 ///
239 /// Attrs::new()
240 /// .once("rename_all", with::eq(set::from_str(&mut rename_all)))
241 /// .once("untagged", set::flag(&mut untagged))
242 /// .once("deny_unknown_fields", set::flag(&mut deny_unknown_fields))
243 /// .parse_attrs("serde", &attrs)?;
244 ///
245 /// assert!(rename_all.is_some() && untagged && deny_unknown_fields);
246 /// # Ok(()) }
247 /// ```
248 pub fn parse_attrs<Q>(&mut self, path: &Q, attrs: &[Attribute]) -> syn::Result<()>
249 where
250 Q: ?Sized,
251 Ident: PartialEq<Q>,
252 {
253 for attr in attrs {
254 if attr.path().is_ident(path) {
255 attr.parse_args_with(&mut *self)?
256 }
257 }
258 Ok(())
259 }
260
261 /// Parse and remove all the [`Attribute`]s where their path is the given `path`.
262 ///
263 /// ```
264 /// # fn main() -> syn::Result<()> {
265 /// # use syn::*;
266 /// # use attrs::*;
267 ///
268 /// let mut rename_all = None::<String>;
269 /// let mut untagged = false;
270 /// let mut deny_unknown_fields = false;
271 /// let mut attrs: Vec<Attribute> = parse_quote! {
272 /// #[serde(rename_all = "kebab-case", untagged)]
273 /// #[default] // SKIPPED
274 /// #[serde(deny_unknown_fields)]
275 /// };
276 ///
277 /// Attrs::new()
278 /// .once("rename_all", with::eq(set::from_str(&mut rename_all)))
279 /// .once("untagged", set::flag(&mut untagged))
280 /// .once("deny_unknown_fields", set::flag(&mut deny_unknown_fields))
281 /// .extract_from("serde", &mut attrs)?;
282 ///
283 /// assert!(rename_all.is_some() && untagged && deny_unknown_fields);
284 /// assert_eq!(attrs.len(), 1); // `#[default]` is still there
285 /// # Ok(()) }
286 /// ```
287 pub fn extract_from<Q>(&mut self, path: &Q, attrs: &mut Vec<Attribute>) -> syn::Result<()>
288 where
289 Q: ?Sized,
290 Ident: PartialEq<Q>,
291 {
292 let mut e = None;
293 attrs.retain(|attr| match attr.path().is_ident(path) {
294 true => {
295 match (e.as_mut(), attr.parse_args_with(&mut *self)) {
296 (_, Ok(())) => {}
297 (None, Err(e2)) => e = Some(e2),
298 (Some(e1), Err(e2)) => e1.combine(e2),
299 }
300 false // parsed - remove from `attrs`
301 }
302 false => true, // not ours - leave in `attrs`
303 });
304 e.map(Err).unwrap_or(Ok(()))
305 }
306
307 /// Parse the entirety of input as a sequence of registered `key`s,
308 /// followed by the appropriate combinator,
309 /// separated by commas.
310 fn _parse(&mut self, input: ParseStream<'_>) -> syn::Result<()> {
311 let msg = Phrase {
312 many: "Expected one of",
313 one: "Expected",
314 none: match &self.fallback {
315 Some(_) => "No explicit arguments specified",
316 None => "No arguments accepted",
317 },
318 conjunction: "or",
319 iter: self
320 .map
321 .iter()
322 .filter_map(|(k, v)| match v {
323 Attr::AliasFor(_) => None,
324 Attr::Once(_) | Attr::Many(_) => Some(k.clone()),
325 })
326 .collect::<Vec<_>>(),
327 };
328 // parse input
329 loop {
330 if input.is_empty() {
331 break;
332 }
333 match input.call(Ident::parse_any) {
334 Ok(it) => {
335 let mut key = it.unraw();
336 // follow redirects
337 loop {
338 break match (self.map.get_mut(&key), &mut self.fallback) {
339 (Some(attr), _) => match attr {
340 Attr::AliasFor(redirect) => {
341 key = redirect.clone();
342 continue;
343 }
344 Attr::Once(once) => {
345 match mem::replace(once, Once::Already(it.span())) {
346 Once::Some(f) => f(input)?,
347 Once::Already(already) => {
348 let mut e =
349 syn::Error::new(it.span(), "Duplicate argument");
350 e.combine(syn::Error::new(
351 already,
352 "Already used here",
353 ));
354 return Err(e);
355 }
356 }
357 }
358 Attr::Many(f) => f(input)?,
359 },
360 (None, Some(fallback)) => match fallback(&key, input) {
361 Ok(()) => {}
362 Err(mut e) => {
363 e.combine(syn::Error::new(e.span(), msg));
364 return Err(e);
365 }
366 },
367 (None, None) => return Err(syn::Error::new(it.span(), msg)),
368 };
369 }
370 }
371 Err(mut e) => {
372 e.combine(syn::Error::new(e.span(), msg));
373 return Err(e);
374 }
375 }
376 if input.is_empty() {
377 break;
378 }
379 input.parse::<Token![,]>()?;
380 }
381 Ok(())
382 }
383
384 #[track_caller]
385 fn insert(&mut self, key: impl UnwrapIdent, val: Attr<'a>) -> &mut Self {
386 match self.map.entry(key.unwrap_ident()) {
387 Entry::Vacant(it) => it.insert(val),
388 Entry::Occupied(it) => panic!("duplicate entry for key `{}`", it.key()),
389 };
390 self
391 }
392
393 fn into_parser(mut self) -> impl FnMut(ParseStream<'_>) -> syn::Result<()> {
394 move |input| self._parse(input)
395 }
396 fn as_parser(&mut self) -> impl FnMut(ParseStream<'_>) -> syn::Result<()> {
397 |input| self._parse(input)
398 }
399}
400
401enum Attr<'a> {
402 AliasFor(Ident),
403 Once(Once<'a>),
404 Many(Box<dyn 'a + FnMut(ParseStream<'_>) -> syn::Result<()>>),
405}
406
407impl fmt::Debug for Attr<'_> {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 match self {
410 Self::AliasFor(it) => f.debug_tuple("AliasFor").field(it).finish(),
411 Self::Once(it) => f.debug_tuple("Once").field(it).finish(),
412 Self::Many(_) => f.debug_tuple("Many").finish_non_exhaustive(),
413 }
414 }
415}
416
417enum Once<'a> {
418 Some(Box<dyn 'a + FnOnce(ParseStream<'_>) -> syn::Result<()>>),
419 Already(Span),
420}
421impl fmt::Debug for Once<'_> {
422 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 match self {
424 Self::Some(_) => f.debug_tuple("Some").finish_non_exhaustive(),
425 Self::Already(span) => f.debug_tuple("Already").field(span).finish(),
426 }
427 }
428}
429
430/// Borrow from this object as a [`Parser`].
431///
432/// ```
433/// # fn main() -> syn::Result<()> {
434/// # use attrs::*;
435/// # use syn::*;
436/// use syn::parse::Parser as _;
437///
438/// let mut untagged = false;
439/// let mut krate = None::<Path>;
440///
441/// Attrs::new()
442/// .once("untagged", set::flag(&mut untagged))
443/// .once("crate", with::eq(set::parse_str(&mut krate)))
444/// .parse_str(r#"untagged, crate = "path::to::serde""#)?;
445///
446/// assert!(krate.is_some() && untagged);
447/// # Ok(()) }
448/// ```
449impl Parser for &mut Attrs<'_> {
450 type Output = ();
451 fn parse2(self, tokens: TokenStream) -> syn::Result<Self::Output> {
452 self.as_parser().parse2(tokens)
453 }
454}
455
456/// Move this object into a [`Parser`].
457///
458/// ```
459/// # fn main() -> syn::Result<()> {
460/// # use attrs::*;
461/// # use syn::*;
462/// use syn::parse::Parser as _;
463///
464/// let mut untagged = false;
465/// let mut krate = None::<Path>;
466///
467/// let mut attrs = Attrs::new();
468/// attrs
469/// .once("untagged", set::flag(&mut untagged))
470/// .once("crate", with::eq(set::parse_str(&mut krate)));
471/// attrs.parse_str(r#"untagged, crate = "path::to::serde""#)?;
472///
473/// assert!(krate.is_some() && untagged);
474/// # Ok(()) }
475/// ```
476impl Parser for Attrs<'_> {
477 type Output = ();
478 fn parse2(self, tokens: TokenStream) -> syn::Result<Self::Output> {
479 self.into_parser().parse2(tokens)
480 }
481}
482
483#[test]
484fn test() {
485 use quote::quote;
486 use syn::{punctuated::Punctuated, *};
487
488 strum_lite::strum! {
489 #[derive(PartialEq, Debug)]
490 enum Casing {
491 Pascal = "PascalCase",
492 Snake = "snake_case",
493 }
494 }
495
496 let mut casing = Casing::Snake;
497 let mut vis = Visibility::Inherited;
498 let mut opt_pred = None::<WherePredicate>;
499 let mut use_unsafe = false;
500 let mut aliases = vec![];
501
502 Attrs::new()
503 // `rename_all = "snake_case"`
504 .once("rename_all", with::eq(on::from_str(&mut casing)))
505 // `vis = pub` or `vis(pub)`
506 .once("vis", with::peq(on::parse(&mut vis)))
507 // `use_unsafe`
508 .once("use_unsafe", set::flag(&mut use_unsafe))
509 // `where(T: Foo)`
510 .once("where", with::paren(set::parse(&mut opt_pred)))
511 // `alias("hello", "world"), alias("goodbye")`
512 .many(
513 "alias",
514 with::paren(|input| {
515 aliases.extend(Punctuated::<LitStr, Token![,]>::parse_separated_nonempty(
516 input,
517 )?);
518 Ok(())
519 }),
520 )
521 .parse2(quote! {
522 rename_all = "PascalCase",
523 vis = pub,
524 use_unsafe,
525 where(T: Ord),
526 alias("hello", "world"),
527 alias("goodbye")
528 })
529 .unwrap();
530 assert_eq!(casing, Casing::Pascal);
531 assert!(matches!(vis, Visibility::Public(_)));
532 assert!(opt_pred.is_some());
533 assert!(use_unsafe);
534 assert_eq!(aliases.len(), 3);
535}
536
537/// Conversion to an [`Ident`].
538///
539/// This is primarily an ergonomic aid, and SHOULD NOT be used on untrusted inputs.
540pub trait UnwrapIdent {
541 /// # Panics
542 /// - Implementors may decide to panic.
543 #[track_caller]
544 fn unwrap_ident(&self) -> Ident;
545}
546
547impl UnwrapIdent for str {
548 #[track_caller]
549 fn unwrap_ident(&self) -> Ident {
550 Ident::new(self, Span::call_site())
551 }
552}
553impl UnwrapIdent for String {
554 #[track_caller]
555 fn unwrap_ident(&self) -> Ident {
556 <str>::unwrap_ident(self)
557 }
558}
559impl UnwrapIdent for Cow<'_, str> {
560 #[track_caller]
561 fn unwrap_ident(&self) -> Ident {
562 <str>::unwrap_ident(self)
563 }
564}
565impl UnwrapIdent for Ident {
566 #[track_caller]
567 fn unwrap_ident(&self) -> Ident {
568 self.clone()
569 }
570}
571impl<T: UnwrapIdent + ?Sized> UnwrapIdent for &T {
572 #[track_caller]
573 fn unwrap_ident(&self) -> Ident {
574 T::unwrap_ident(self)
575 }
576}
577impl<T: UnwrapIdent + ?Sized> UnwrapIdent for Box<T> {
578 #[track_caller]
579 fn unwrap_ident(&self) -> Ident {
580 T::unwrap_ident(self)
581 }
582}
583impl<T: UnwrapIdent + ?Sized> UnwrapIdent for Rc<T> {
584 #[track_caller]
585 fn unwrap_ident(&self) -> Ident {
586 T::unwrap_ident(self)
587 }
588}
589impl<T: UnwrapIdent + ?Sized> UnwrapIdent for Arc<T> {
590 #[track_caller]
591 fn unwrap_ident(&self) -> Ident {
592 T::unwrap_ident(self)
593 }
594}
595
596/// Wrap parsing functions so that they are e.g preceded by `=` or surrounded by `(..)`.
597pub mod with {
598 use syn::{
599 Token, braced, bracketed, parenthesized,
600 parse::{ParseStream, discouraged::AnyDelimiter},
601 token,
602 };
603
604 /// Take an `=` before appling `f`.
605 ///
606 /// Users should be careful that `f` doesn't consume too far into the input.
607 pub fn eq<'a, F>(mut f: F) -> impl 'a + FnMut(ParseStream<'_>) -> syn::Result<()>
608 where
609 F: 'a + FnMut(ParseStream<'_>) -> syn::Result<()>,
610 {
611 move |input| {
612 input.parse::<Token![=]>()?;
613 f(input)
614 }
615 }
616 /// Take a `(...)`, appling `f` to its contents.
617 pub fn paren<'a, F>(mut f: F) -> impl 'a + FnMut(ParseStream<'_>) -> syn::Result<()>
618 where
619 F: 'a + FnMut(ParseStream<'_>) -> syn::Result<()>,
620 {
621 move |input| {
622 let content;
623 parenthesized!(content in input);
624 f(&content)
625 }
626 }
627 /// Take a `[...]`, appling `f` to its contents.
628 pub fn bracket<'a, F>(mut f: F) -> impl 'a + FnMut(ParseStream<'_>) -> syn::Result<()>
629 where
630 F: 'a + FnMut(ParseStream<'_>) -> syn::Result<()>,
631 {
632 move |input| {
633 let content;
634 bracketed!(content in input);
635 f(&content)
636 }
637 }
638 /// Take a `{...}`, appling `f` to its contents.
639 pub fn brace<'a, F>(mut f: F) -> impl 'a + FnMut(ParseStream<'_>) -> syn::Result<()>
640 where
641 F: 'a + FnMut(ParseStream<'_>) -> syn::Result<()>,
642 {
643 move |input| {
644 let content;
645 braced!(content in input);
646 f(&content)
647 }
648 }
649 /// Take any group (`(...)`, `[...]`, `{...}`), appling `f` to its contents.
650 pub fn delim<'a, F>(mut f: F) -> impl 'a + FnMut(ParseStream<'_>) -> syn::Result<()>
651 where
652 F: 'a + FnMut(ParseStream<'_>) -> syn::Result<()>,
653 {
654 move |input| {
655 let (_, _, content) = input.parse_any_delimiter()?;
656 f(&content)
657 }
658 }
659
660 /// Either:
661 /// - Take an `=` before applying `f`.
662 /// See also [`eq`].
663 /// - Take a `(...)` before applying `f` to its contents.
664 pub fn peq<'a, F>(mut f: F) -> impl 'a + FnMut(ParseStream<'_>) -> syn::Result<()>
665 where
666 F: 'a + FnMut(ParseStream<'_>) -> syn::Result<()>,
667 {
668 move |input| {
669 if input.peek(Token![=]) {
670 input.parse::<Token![=]>()?;
671 f(input)
672 } else if input.peek(token::Paren) {
673 let content;
674 parenthesized!(content in input);
675 f(&content)
676 } else {
677 Err(input.error("Expected a `=` or `(..)`"))
678 }
679 }
680 }
681}
682
683/// Create [`Parser`]s that write to [`&mut Option<T>`](Option).
684pub mod set {
685 use super::*;
686
687 #[deprecated = "use `flag::free` instead"]
688 pub use flag::free as flag;
689
690 /// Parse a [`LitBool`], assigning it to `dst` in [`Some`].
691 ///
692 /// ```
693 /// #![expect(deprecated)]
694 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
695 /// # fn main() -> syn::Result<()> {
696 /// let mut val = None;
697 ///
698 /// Attrs::new()
699 /// .once("key", with::eq(set::bool(&mut val)))
700 /// .parse2(quote!(key = true))?;
701 ///
702 /// assert_eq!(val, Some(true));
703 /// # Ok(()) }
704 /// ```
705 #[deprecated = "Use `set::lit` instead"]
706 pub fn bool(dst: &mut Option<bool>) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
707 |input| parse::set::lit(dst, input)
708 }
709 /// Parse a [`Parse`]-able, assigning it to `dst` in [`Some`].
710 ///
711 /// You should take care that the [`Parse`] does not advance into subsequent keys,
712 /// else use [`parse_str`] instead.
713 ///
714 /// ```
715 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
716 /// # use proc_macro2::{Span, Ident};
717 /// # fn main() -> syn::Result<()> {
718 /// let mut val = None::<Ident>;
719 ///
720 /// Attrs::new()
721 /// .once("key", with::eq(set::parse(&mut val)))
722 /// .parse2(quote!(key = Value))?;
723 ///
724 /// assert_eq!(val, Some(Ident::new("Value", Span::call_site())));
725 /// # Ok(()) }
726 /// ```
727 pub fn parse<T: Parse>(
728 dst: &mut Option<T>,
729 ) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
730 |input| parse::set::parse(dst, input)
731 }
732 /// 1. Parse a [`LitStr`].
733 /// 2. Parse the contents of that string using [`FromStr`].
734 /// 3. Assign the result to `dst` in [`Some`].
735 ///
736 /// ```
737 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
738 /// # use core::net::Ipv4Addr;
739 /// # fn main() -> syn::Result<()> {
740 /// let mut val = None::<Ipv4Addr>;
741 ///
742 /// Attrs::new()
743 /// .once("key", with::eq(set::from_str(&mut val)))
744 /// .parse2(quote!(key = "127.0.0.1"))?;
745 ///
746 /// assert_eq!(val, Some(Ipv4Addr::LOCALHOST));
747 /// # Ok(()) }
748 /// ```
749 pub fn from_str<T: FromStr>(
750 dst: &mut Option<T>,
751 ) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()>
752 where
753 T::Err: Display,
754 {
755 |input| parse::set::from_str(dst, input)
756 }
757 /// 1. Parse a [`LitStr`].
758 /// 2. Parse the contents of that string into the [`Parse`]-able.
759 /// 3. Assign the result to `dst` in [`Some`].
760 ///
761 /// ```
762 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
763 /// # use proc_macro2::{Span, Ident};
764 /// # fn main() -> syn::Result<()> {
765 /// let mut val = None::<Ident>;
766 ///
767 /// Attrs::new()
768 /// .once("key", with::eq(set::parse_str(&mut val)))
769 /// .parse2(quote!(key = "Value"))?;
770 ///
771 /// assert_eq!(val, Some(Ident::new("Value", Span::call_site())));
772 /// # Ok(()) }
773 /// ```
774 pub fn parse_str<T: Parse>(
775 dst: &mut Option<T>,
776 ) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
777 |input| parse::set::parse_str(dst, input)
778 }
779
780 /// If `input` contains a [`LitStr`],
781 /// then parse the contents of that string into the [`Parse`]-able in [`Some`].
782 /// Else, directly parse it from `input`.
783 ///
784 /// ```
785 /// # use {attrs::*, syn::{parse::Parser as _, parse_quote}, quote::quote};
786 /// # use proc_macro2::{Span, Ident};
787 /// # fn main() -> syn::Result<()> {
788 /// let mut raw = None::<syn::Path>;
789 /// let mut str = None::<syn::Path>;
790 ///
791 /// Attrs::new()
792 /// .once("raw", with::eq(set::maybe_str(&mut raw)))
793 /// .once("str", with::eq(set::maybe_str(&mut str)))
794 /// .parse2(quote!(raw = raw::path, str = "str::path"))?;
795 ///
796 /// assert_eq!(raw, Some(parse_quote!(raw::path)));
797 /// assert_eq!(str, Some(parse_quote!(str::path)));
798 /// # Ok(()) }
799 /// ```
800 pub fn maybe_str<T: Parse>(
801 dst: &mut Option<T>,
802 ) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
803 |input| parse::set::maybe_str(dst, input)
804 }
805
806 /// Parse the appropriate [`syn::Lit`],
807 /// extracting the value,
808 /// and assigning the result to `dst` in [`Some`].
809 ///
810 /// ```
811 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
812 /// # use proc_macro2::{Span, Ident};
813 /// # fn main() -> syn::Result<()> {
814 /// let mut byte = None::<u8>;
815 /// let mut usize = None::<usize>;
816 /// let mut isize = None::<isize>;
817 /// let mut float = None::<f32>;
818 /// let mut bool = None::<bool>;
819 /// let mut char = None::<char>;
820 /// let mut string = None::<String>;
821 /// let mut bytes = None::<Vec<u8>>;
822 ///
823 /// Attrs::new()
824 /// .once("byte", with::eq(set::lit(&mut byte)))
825 /// .once("usize", with::eq(set::lit(&mut usize)))
826 /// .once("isize", with::eq(set::lit(&mut isize)))
827 /// .once("float", with::eq(set::lit(&mut float)))
828 /// .once("bool", with::eq(set::lit(&mut bool)))
829 /// .once("char", with::eq(set::lit(&mut char)))
830 /// .once("string", with::eq(set::lit(&mut string)))
831 /// .once("bytes", with::eq(set::lit(&mut bytes)))
832 /// .parse2(quote! {
833 /// byte = b'A',
834 /// usize = 123,
835 /// isize = -456,
836 /// float = 7.89,
837 /// bool = false,
838 /// char = 'ð“€€',
839 /// string = "hello",
840 /// bytes = b"world",
841 /// })?;
842 ///
843 /// assert_eq!(byte, Some(b'A'));
844 /// assert_eq!(usize, Some(123));
845 /// assert_eq!(isize, Some(-456));
846 /// assert_eq!(float, Some(7.89));
847 /// assert_eq!(bool, Some(false));
848 /// assert_eq!(char, Some('ð“€€'));
849 /// assert_eq!(string, Some(String::from("hello")));
850 /// assert_eq!(bytes, Some(Vec::from(b"world")));
851 /// # Ok(()) }
852 /// ```
853 pub fn lit<T: Lit>(dst: &mut Option<T>) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
854 |input| parse::set::lit(dst, input)
855 }
856}
857
858/// Create [`Parser`]s that write to `&mut T` when they parse.
859pub mod on {
860 use super::*;
861
862 /// Parse a [`LitBool`], assigning its value to `dst`.
863 ///
864 /// ```
865 /// #![expect(deprecated)]
866 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
867 /// # fn main() -> syn::Result<()> {
868 /// let mut val = false;
869 ///
870 /// Attrs::new()
871 /// .once("key", with::eq(on::bool(&mut val)))
872 /// .parse2(quote!(key = true))?;
873 ///
874 /// assert!(val);
875 /// # Ok(()) }
876 #[deprecated = "Use `on::lit` instead"]
877 pub fn bool(dst: &mut bool) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
878 |input| parse::lit(dst, input)
879 }
880 /// Parse a [`Parse`]-able, assigning its value to `dst`.
881 ///
882 /// You should take care that the [`Parse`] does not advance into subsequent keys,
883 /// else use [`parse_str`] instead.
884 ///
885 /// ```
886 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
887 /// # use proc_macro2::{Span, Ident};
888 /// # fn main() -> syn::Result<()> {
889 /// let mut val = Ident::new("Default", Span::call_site());
890 ///
891 /// Attrs::new()
892 /// .once("key", with::eq(on::parse(&mut val)))
893 /// .parse2(quote!(key = Override))?;
894 ///
895 /// assert_eq!(val, Ident::new("Override", Span::call_site()));
896 /// # Ok(()) }
897 /// ```
898 pub fn parse<T: Parse>(dst: &mut T) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
899 |input| parse::parse(dst, input)
900 }
901 /// 1. Parse a [`LitStr`].
902 /// 2. Parse the contents of that string using [`FromStr`].
903 /// 3. Assign the result to `dst`.
904 ///
905 /// ```
906 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
907 /// # use core::net::Ipv4Addr;
908 /// # fn main() -> syn::Result<()> {
909 /// let mut val = Ipv4Addr::BROADCAST;
910 ///
911 /// Attrs::new()
912 /// .once("key", with::eq(on::from_str(&mut val)))
913 /// .parse2(quote!(key = "127.0.0.1"))?;
914 ///
915 /// assert_eq!(val, Ipv4Addr::LOCALHOST);
916 /// # Ok(()) }
917 /// ```
918 pub fn from_str<T: FromStr>(dst: &mut T) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()>
919 where
920 T::Err: Display,
921 {
922 |input| parse::from_str(dst, input)
923 }
924 /// 1. Parse a [`LitStr`].
925 /// 2. Parse the contents of that string into the [`Parse`]-able.
926 /// 3. Assign the result to `dst`.
927 ///
928 /// ```
929 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
930 /// # use proc_macro2::{Span, Ident};
931 /// # fn main() -> syn::Result<()> {
932 /// let mut val = Ident::new("Default", Span::call_site());
933 ///
934 /// Attrs::new()
935 /// .once("key", with::eq(on::parse_str(&mut val)))
936 /// .parse2(quote!(key = "Override"))?;
937 ///
938 /// assert_eq!(val, Ident::new("Override", Span::call_site()));
939 /// # Ok(()) }
940 /// ```
941 pub fn parse_str<T: Parse>(dst: &mut T) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
942 |input| parse::parse_str(dst, input)
943 }
944
945 /// If `input` contains a [`LitStr`],
946 /// then parse the contents of that string into the [`Parse`]-able.
947 /// Else, directly parse it from `input`.
948 ///
949 /// ```
950 /// # use {attrs::*, syn::{parse::Parser as _, parse_quote}, quote::quote};
951 /// # fn main() -> syn::Result<()> {
952 /// let mut raw: syn::Path = parse_quote!(default::path);
953 /// let mut str: syn::Path = parse_quote!(default::path);
954 ///
955 /// Attrs::new()
956 /// .once("raw", with::eq(on::maybe_str(&mut raw)))
957 /// .once("str", with::eq(on::maybe_str(&mut str)))
958 /// .parse2(quote!(raw = raw::path, str = "str::path"))?;
959 ///
960 /// assert_eq!(raw, parse_quote!(raw::path));
961 /// assert_eq!(str, parse_quote!(str::path));
962 /// # Ok(()) }
963 /// ```
964 pub fn maybe_str<T: Parse>(dst: &mut T) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
965 |input| parse::maybe_str(dst, input)
966 }
967
968 /// Parse the appropriate [`syn::Lit`] from `input`,
969 /// extracting the value,
970 /// and assigning the result to `dst`.
971 ///
972 /// ```
973 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
974 /// # use proc_macro2::{Span, Ident};
975 /// # fn main() -> syn::Result<()> {
976 /// let mut byte = 0u8;
977 /// let mut usize = 0usize;
978 /// let mut isize = 0isize;
979 /// let mut float = 0.0f32;
980 /// let mut bool = false;
981 /// let mut char = 'a';
982 /// let mut string = String::new();
983 /// let mut bytes = Vec::new();
984 ///
985 /// Attrs::new()
986 /// .once("byte", with::eq(on::lit(&mut byte)))
987 /// .once("usize", with::eq(on::lit(&mut usize)))
988 /// .once("isize", with::eq(on::lit(&mut isize)))
989 /// .once("float", with::eq(on::lit(&mut float)))
990 /// .once("bool", with::eq(on::lit(&mut bool)))
991 /// .once("char", with::eq(on::lit(&mut char)))
992 /// .once("string", with::eq(on::lit(&mut string)))
993 /// .once("bytes", with::eq(on::lit(&mut bytes)))
994 /// .parse2(quote! {
995 /// byte = b'A',
996 /// usize = 123,
997 /// isize = -456,
998 /// float = 7.89,
999 /// bool = false,
1000 /// char = 'ð“€€',
1001 /// string = "hello",
1002 /// bytes = b"world",
1003 /// })?;
1004 ///
1005 /// assert_eq!(byte, b'A');
1006 /// assert_eq!(usize, 123);
1007 /// assert_eq!(isize, -456);
1008 /// assert_eq!(float, 7.89);
1009 /// assert_eq!(bool, false);
1010 /// assert_eq!(char, 'ð“€€');
1011 /// assert_eq!(string, "hello");
1012 /// assert_eq!(bytes, b"world");
1013 /// # Ok(()) }
1014 /// ```
1015 pub fn lit<T: Lit>(dst: &mut T) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
1016 |input| parse::lit(dst, input)
1017 }
1018}
1019
1020/// Create [`Parser`]s which set [`bool`]s.
1021pub mod flag {
1022 use syn::token;
1023
1024 use super::*;
1025
1026 /// Ignores the input, and just sets `dst` to `true`.
1027 ///
1028 /// ```
1029 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
1030 /// # fn main() -> syn::Result<()> {
1031 /// let mut val = false;
1032 ///
1033 /// Attrs::new()
1034 /// .once("bare", flag::free(&mut val))
1035 /// .parse2(quote!(bare))?;
1036 ///
1037 /// assert!(val);
1038 /// # Ok(()) }
1039 /// ```
1040 pub fn free(dst: &mut bool) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
1041 |_| {
1042 *dst = true;
1043 Ok(())
1044 }
1045 }
1046 /// Accept `key` or `key = true`
1047 ///
1048 /// ```
1049 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
1050 /// # fn main() -> syn::Result<()> {
1051 /// let mut val1 = false;
1052 /// let mut val2 = true;
1053 ///
1054 /// Attrs::new()
1055 /// .once("bare", flag::or_eq(&mut val1))
1056 /// .once("explicit", flag::or_eq(&mut val2))
1057 /// .parse2(quote!(bare, explicit = false))?;
1058 ///
1059 /// assert!(val1);
1060 /// assert!(!val2);
1061 /// # Ok(()) }
1062 /// ```
1063 pub fn or_eq(dst: &mut bool) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
1064 |input| match input.peek(Token![=]) {
1065 true => with::eq(on::lit(dst))(input),
1066 false => free(dst)(input),
1067 }
1068 }
1069 /// Accept `key` or `key(true)`
1070 ///
1071 /// ```
1072 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
1073 /// # fn main() -> syn::Result<()> {
1074 /// let mut val1 = false;
1075 /// let mut val2 = true;
1076 ///
1077 /// Attrs::new()
1078 /// .once("bare", flag::or_paren(&mut val1))
1079 /// .once("explicit", flag::or_paren(&mut val2))
1080 /// .parse2(quote!(bare, explicit(false)))?;
1081 ///
1082 /// assert!(val1);
1083 /// assert!(!val2);
1084 /// # Ok(()) }
1085 /// ```
1086 pub fn or_paren(dst: &mut bool) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
1087 |input| match input.peek(token::Paren) {
1088 true => with::paren(on::lit(dst))(input),
1089 false => free(dst)(input),
1090 }
1091 }
1092 /// Accept `key`, `key = true` or `key(true)`
1093 ///
1094 /// ```
1095 /// # use {attrs::*, syn::parse::Parser as _, quote::quote};
1096 /// # fn main() -> syn::Result<()> {
1097 /// let mut val1 = false;
1098 /// let mut val2 = true;
1099 /// let mut val3 = true;
1100 ///
1101 /// Attrs::new()
1102 /// .once("bare", flag::or_peq(&mut val1))
1103 /// .once("eq", flag::or_peq(&mut val2))
1104 /// .once("paren", flag::or_peq(&mut val3))
1105 /// .parse2(quote!(bare, eq = false, paren(false)))?;
1106 ///
1107 /// assert!(val1);
1108 /// assert!(!val2);
1109 /// assert!(!val3);
1110 /// # Ok(()) }
1111 pub fn or_peq(dst: &mut bool) -> impl '_ + FnMut(ParseStream<'_>) -> syn::Result<()> {
1112 |input| match input.peek(Token![=]) || input.peek(token::Paren) {
1113 true => with::peq(on::lit(dst))(input),
1114 false => free(dst)(input),
1115 }
1116 }
1117}
1118
1119/// Straightforward parsing functions.
1120///
1121/// Useful for constructing your own leaf combinators.
1122pub mod parse {
1123 use super::*;
1124
1125 /// Parse a [`LitBool`] from `input`, assigning it to `dst`.
1126 #[deprecated = "Use `parse::lit` instead"]
1127 pub fn bool(dst: &mut bool, input: ParseStream<'_>) -> syn::Result<()> {
1128 *dst = input.parse::<LitBool>()?.value;
1129 Ok(())
1130 }
1131 /// Parse a [`Parse`]-able from `input`, assigning it to `dst`.
1132 pub fn parse<T: Parse>(dst: &mut T, input: ParseStream<'_>) -> syn::Result<()> {
1133 *dst = input.parse()?;
1134 Ok(())
1135 }
1136 /// 1. Parse a [`LitStr`] from `input`.
1137 /// 2. Parse the contents of that string using [`FromStr`].
1138 /// 3. Assign the result to `dst`.
1139 pub fn from_str<T: FromStr>(dst: &mut T, input: ParseStream<'_>) -> syn::Result<()>
1140 where
1141 T::Err: Display,
1142 {
1143 let lit_str = input.parse::<LitStr>()?;
1144 match lit_str.value().parse() {
1145 Ok(it) => {
1146 *dst = it;
1147 Ok(())
1148 }
1149 Err(e) => Err(syn::Error::new(lit_str.span(), e)),
1150 }
1151 }
1152 /// 1. Parse a [`LitStr`] from `input`.
1153 /// 2. Parse the contents of that string into the [`Parse`]-able.
1154 /// 3. Assign the result to `dst`.
1155 pub fn parse_str<T: Parse>(dst: &mut T, input: ParseStream<'_>) -> syn::Result<()> {
1156 let lit_str = input.parse::<LitStr>()?;
1157 *dst = T::parse.parse_str(&lit_str.value())?;
1158 Ok(())
1159 }
1160 /// If `input` contains a [`LitStr`],
1161 /// then parse the contents of that string into the [`Parse`]-able.
1162 /// Else, directly parse it from `input`.
1163 pub fn maybe_str<T: Parse>(dst: &mut T, input: ParseStream<'_>) -> syn::Result<()> {
1164 *dst = match input.peek(LitStr) {
1165 true => input.parse::<LitStr>()?.parse()?,
1166 false => input.parse()?,
1167 };
1168 Ok(())
1169 }
1170 /// Parse the appropriate [`syn::Lit`] from `input`,
1171 /// extracting the value,
1172 /// and assigning the result to `dst`.
1173 pub fn lit<T: Lit>(dst: &mut T, input: ParseStream<'_>) -> syn::Result<()> {
1174 *dst = Lit::parse(input)?;
1175 Ok(())
1176 }
1177
1178 /// Collect from `input` until a `,` is encountered.
1179 pub fn until_comma(input: ParseStream<'_>) -> syn::Result<TokenStream> {
1180 input.step(|cursor| {
1181 let mut tokens = TokenStream::new();
1182 let mut rest = *cursor;
1183 while let Some((tt, cursor)) = rest.token_tree() {
1184 rest = cursor;
1185 match tt {
1186 proc_macro2::TokenTree::Punct(it) if it.as_char() == ',' => break,
1187 tt => tokens.extend([tt]),
1188 };
1189 }
1190 Ok((tokens, rest))
1191 })
1192 }
1193
1194 /// Straightforward parsing functions that set [`Option`]s.
1195 pub mod set {
1196 use super::*;
1197
1198 /// Parse a [`LitBool`] from `input`, assigning it to `dst` in [`Some`].
1199 #[deprecated = "Use `parse::set::lit` instead"]
1200 pub fn bool(dst: &mut Option<bool>, input: ParseStream<'_>) -> syn::Result<()> {
1201 *dst = Some(input.parse::<LitBool>()?.value);
1202 Ok(())
1203 }
1204 /// Parse a [`Parse`]-able from `input`, assigning it to `dst` in [`Some`].
1205 pub fn parse<T: Parse>(dst: &mut Option<T>, input: ParseStream<'_>) -> syn::Result<()> {
1206 *dst = Some(input.parse()?);
1207 Ok(())
1208 }
1209 /// 1. Parse a [`LitStr`] from `input`.
1210 /// 2. Parse the contents of that string using [`FromStr`].
1211 /// 3. Assign the result to `dst` in [`Some`].
1212 pub fn from_str<T: FromStr>(dst: &mut Option<T>, input: ParseStream<'_>) -> syn::Result<()>
1213 where
1214 T::Err: Display,
1215 {
1216 let lit_str = input.parse::<LitStr>()?;
1217 match lit_str.value().parse() {
1218 Ok(it) => {
1219 *dst = Some(it);
1220 Ok(())
1221 }
1222 Err(e) => Err(syn::Error::new(lit_str.span(), e)),
1223 }
1224 }
1225 /// 1. Parse a [`LitStr`] from `input`.
1226 /// 2. Parse the contents of that string into the [`Parse`]-able.
1227 /// 3. Assign the result to `dst` in [`Some`].
1228 pub fn parse_str<T: Parse>(dst: &mut Option<T>, input: ParseStream<'_>) -> syn::Result<()> {
1229 *dst = Some(input.parse::<LitStr>()?.parse()?);
1230 Ok(())
1231 }
1232
1233 /// If `input` contains a [`LitStr`],
1234 /// then parse the contents of that string into the [`Parse`]-able in [`Some`].
1235 /// Else, directly parse it from `input`.
1236 pub fn maybe_str<T: Parse>(dst: &mut Option<T>, input: ParseStream<'_>) -> syn::Result<()> {
1237 *dst = Some(match input.peek(LitStr) {
1238 true => input.parse::<LitStr>()?.parse()?,
1239 false => input.parse()?,
1240 });
1241 Ok(())
1242 }
1243 /// Parse the appropriate [`syn::Lit`] from `input`,
1244 /// extracting the value,
1245 /// and assigning the result to `dst` in [`Some`].
1246 pub fn lit<T: Lit>(dst: &mut Option<T>, input: ParseStream<'_>) -> syn::Result<()> {
1247 *dst = Some(Lit::parse(input)?);
1248 Ok(())
1249 }
1250 }
1251}
1252
1253/// A value that can be parsed by this crate from a [`syn::Lit`].
1254///
1255/// See [`set::lit`] and [`on::lit`] for usage examples.
1256///
1257/// This trait is sealed, and cannot be implemented for types outside this crate.
1258pub trait Lit: Sized + sealed::Sealed {
1259 fn parse(input: ParseStream<'_>) -> syn::Result<Self>;
1260}
1261
1262mod sealed {
1263 pub trait Sealed {}
1264 macro_rules! sealed {
1265 ($($ty:ty),* $(,)?) => {
1266 $(impl Sealed for $ty {})*
1267 };
1268 }
1269 sealed! {
1270 u8, u16, u32, u64, u128, usize,
1271 i8, i16, i32, i64, i128, isize,
1272 f32, f64,
1273 bool,
1274 char,
1275 String,
1276 Vec<u8>,
1277 }
1278}
1279
1280macro_rules! num {
1281 ($($via:ty {
1282 $($ty:ty),* $(,)?
1283 } )*) => {
1284 $(
1285 $(
1286 impl Lit for $ty {
1287 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
1288 let lit = input.parse::<$via>()?;
1289 match lit.suffix() {
1290 "" | stringify!($ty) => lit.base10_parse(),
1291 _ => Err(syn::Error::new(
1292 lit.span(),
1293 concat!("Expected suffix `", stringify!($ty), "`"),
1294 )),
1295 }
1296 }
1297 }
1298 )*
1299 )*
1300 };
1301 }
1302
1303num! {
1304 syn::LitInt {
1305 u16, u32, u64, u128, usize,
1306 i8, i16, i32, i64, i128, isize,
1307 }
1308 syn::LitFloat {
1309 f32, f64
1310 }
1311}
1312
1313impl Lit for u8 {
1314 /// Note that bytes may parsed from a [`LitInt`](syn::LitInt) or a [`LitByte`](syn::LitByte).
1315 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
1316 match input.parse::<syn::Lit>()? {
1317 syn::Lit::Byte(it) => Ok(it.value()),
1318 syn::Lit::Int(it) => match it.suffix() {
1319 "" | "u8" => it.base10_parse(),
1320 _ => Err(syn::Error::new(it.span(), "Expected suffix `u8`")),
1321 },
1322 other => Err(syn::Error::new(
1323 other.span(),
1324 "Expected a u8 or byte literal",
1325 )),
1326 }
1327 }
1328}
1329
1330impl Lit for bool {
1331 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
1332 Ok(input.parse::<syn::LitBool>()?.value())
1333 }
1334}
1335impl Lit for String {
1336 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
1337 Ok(input.parse::<syn::LitStr>()?.value())
1338 }
1339}
1340impl Lit for Vec<u8> {
1341 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
1342 Ok(input.parse::<syn::LitByteStr>()?.value())
1343 }
1344}
1345impl Lit for char {
1346 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
1347 Ok(input.parse::<syn::LitChar>()?.value())
1348 }
1349}
1350
1351#[derive(Clone, Copy)]
1352struct Phrase<'a, I> {
1353 /// `Expected one of`
1354 pub many: &'a str,
1355 /// `Expected`
1356 pub one: &'a str,
1357 /// `No registered keys`
1358 pub none: &'a str,
1359 /// `or`
1360 pub conjunction: &'a str,
1361 pub iter: I,
1362}
1363
1364impl<I: Clone + IntoIterator> fmt::Display for Phrase<'_, I>
1365where
1366 I::Item: fmt::Display,
1367{
1368 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1369 let Self {
1370 many,
1371 one,
1372 none,
1373 conjunction,
1374 iter,
1375 } = self.clone();
1376 let mut iter = iter.into_iter().peekable();
1377 match iter.next() {
1378 Some(first) => match iter.peek() {
1379 Some(_) => {
1380 f.write_fmt(format_args!("{many} `{first}`"))?;
1381 while let Some(it) = iter.next() {
1382 match iter.peek() {
1383 Some(_) => f.write_fmt(format_args!(", `{it}`"))?,
1384 None => f.write_fmt(format_args!(" {conjunction} `{it}`"))?,
1385 }
1386 }
1387 Ok(())
1388 }
1389 None => f.write_fmt(format_args!("{one} `{first}`")),
1390 },
1391 None => f.write_str(none),
1392 }
1393 }
1394}