1#[cfg(feature = "parsing")]
2use crate::attribute::AttributeExt;
3use crate::ident::GetIdent;
4use crate::path::GetPath;
5use std::collections::HashMap as Map;
6#[cfg(feature = "parsing")]
7use syn::ext::IdentExt;
8#[cfg(feature = "parsing")]
9use syn::parse::Parser;
10#[cfg(feature = "parsing")]
11use syn::Meta as Meta2;
12use syn::{
13 punctuated::Punctuated, token, Attribute, Error, Expr, ExprLit, Ident, Lit, MetaNameValue,
14 Path, Result,
15};
16
17#[derive(Clone)]
18pub enum Meta1 {
19 Path(Path),
20 List(MetaList1),
21 NameValue(MetaNameValue),
22}
23
24#[cfg(feature = "parsing")]
25impl syn::parse::Parse for Meta1 {
26 fn parse(input: syn::parse::ParseStream) -> Result<Self> {
27 let path = input.call(parse_meta_path)?;
28 if input.peek(token::Paren) {
29 Ok(Meta1::List(MetaList1::parse_meta_list_after_path(
30 path, input,
31 )?))
32 } else if input.peek(syn::Token![=]) {
33 Ok(Meta1::NameValue(MetaNameValue {
34 path,
35 eq_token: input.parse()?,
36 value: degroup(input.parse()?),
37 }))
38 } else {
39 Ok(Meta1::Path(path))
40 }
41 }
42}
43
44#[cfg(feature = "parsing")]
45fn degroup(mut expr: Expr) -> Expr {
46 while let Expr::Group(group) = expr {
47 expr = *group.expr
48 }
49 expr
50}
51
52#[cfg(feature = "parsing")]
53impl std::convert::TryFrom<Meta2> for Meta1 {
54 type Error = syn::Error;
55
56 fn try_from(meta: Meta2) -> std::result::Result<Self, Self::Error> {
57 Ok(match meta {
58 Meta2::Path(path) => Meta1::Path(path),
59 Meta2::List(list) => Meta1::List(MetaList1 {
60 path: list.path,
61 paren_token: match list.delimiter {
62 syn::MacroDelimiter::Paren(paren) => paren,
63 other => return Err(syn::Error::new(other.span().open(), "expected paren")),
64 },
65 nested: PunctuatedNestedMeta::parse_terminated.parse2(list.tokens)?,
66 }),
67 Meta2::NameValue(nv) => Meta1::NameValue(nv),
68 })
69 }
70}
71
72impl quote::ToTokens for Meta1 {
73 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
74 match self {
75 Meta1::Path(path) => path.to_tokens(tokens),
76 Meta1::List(list) => list.to_tokens(tokens),
77 Meta1::NameValue(nv) => nv.to_tokens(tokens),
78 }
79 }
80}
81
82impl Meta1 {
83 pub fn path(&self) -> &Path {
84 match self {
85 Meta1::Path(path) => path,
86 Meta1::List(list) => &list.path,
87 Meta1::NameValue(nv) => &nv.path,
88 }
89 }
90}
91
92#[cfg(feature = "parsing")]
93fn parse_meta_path(input: syn::parse::ParseStream) -> Result<Path> {
94 Ok(Path {
95 leading_colon: input.parse()?,
96 segments: {
97 let mut segments = Punctuated::new();
98 while input.peek(Ident::peek_any) {
99 let ident = Ident::parse_any(input)?;
100 segments.push_value(syn::PathSegment::from(ident));
101 if !input.peek(syn::Token![::]) {
102 break;
103 }
104 let punct = input.parse()?;
105 segments.push_punct(punct);
106 }
107 if segments.is_empty() {
108 return Err(input.error("expected path"));
109 } else if segments.trailing_punct() {
110 return Err(input.error("expected path segment"));
111 }
112 segments
113 },
114 })
115}
116
117#[derive(Clone)]
118pub struct MetaList1 {
119 pub path: Path,
120 pub paren_token: token::Paren,
121 pub nested: PunctuatedNestedMeta,
122}
123
124#[cfg(feature = "parsing")]
125impl MetaList1 {
126 fn parse_meta_list_after_path(path: Path, input: syn::parse::ParseStream) -> Result<Self> {
127 let content;
128 Ok(MetaList1 {
129 path,
130 paren_token: syn::parenthesized!(content in input),
131 nested: PunctuatedNestedMeta::parse_terminated(&content)?,
132 })
133 }
134}
135#[cfg(feature = "parsing")]
136impl syn::parse::Parse for MetaList1 {
137 fn parse(input: syn::parse::ParseStream) -> Result<Self> {
138 let path = input.call(parse_meta_path)?;
139 Self::parse_meta_list_after_path(path, input)
140 }
141}
142
143impl quote::ToTokens for MetaList1 {
144 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
145 self.path.to_tokens(tokens);
146 self.paren_token
147 .surround(tokens, |tokens| self.nested.to_tokens(tokens));
148 }
149}
150
151#[derive(Clone)]
152pub enum NestedMeta {
153 Meta(Meta1),
154 Lit(Lit),
155}
156
157#[cfg(feature = "parsing")]
158impl syn::parse::Parse for NestedMeta {
159 fn parse(input: syn::parse::ParseStream) -> Result<Self> {
160 if input.peek(Lit) && !(input.peek(syn::LitBool) && input.peek2(syn::Token![=])) {
161 input.parse().map(NestedMeta::Lit)
162 } else if input.peek(Ident::peek_any)
163 || input.peek(syn::Token![::]) && input.peek3(Ident::peek_any)
164 {
165 input.parse().map(NestedMeta::Meta)
166 } else {
167 Err(input.error("expected identifier or literal"))
168 }
169 }
170}
171
172impl quote::ToTokens for NestedMeta {
173 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
174 match self {
175 NestedMeta::Meta(meta) => meta.to_tokens(tokens),
176 NestedMeta::Lit(lit) => lit.to_tokens(tokens),
177 }
178 }
179}
180
181pub type PunctuatedNestedMeta = Punctuated<NestedMeta, token::Comma>;
183
184pub trait MetaExt {
186 fn is_path(&self) -> bool;
188 fn is_list(&self) -> bool;
190 fn is_name_value(&self) -> bool;
192 fn is_doc(&self) -> bool;
194
195 fn promote_to_list(&mut self, paren: token::Paren) -> Result<&mut MetaList1>;
200
201 fn list(&self) -> Result<&MetaList1>;
203 fn list_mut(&mut self) -> Result<&mut MetaList1>;
205
206 fn name_value(&self) -> Result<&MetaNameValue>;
208 fn name_value_mut(&mut self) -> Result<&mut MetaNameValue>;
210
211 fn doc(&self) -> Result<String>;
213}
214
215pub(crate) fn err_promote_to_list(meta: &Meta1) -> Error {
216 Error::new_spanned(
217 meta,
218 "Only Path can be promoted and List is accepted as non-promoted",
219 )
220}
221
222impl MetaExt for Meta1 {
223 fn is_path(&self) -> bool {
224 matches!(self, Meta1::Path(_))
225 }
226 fn is_list(&self) -> bool {
227 matches!(self, Meta1::List(_))
228 }
229 fn is_name_value(&self) -> bool {
230 matches!(self, Meta1::NameValue(_))
231 }
232 fn is_doc(&self) -> bool {
233 self.name_value().is_ok_and(|v| {
234 v.path.is_ident("doc")
235 && matches!(
236 v.value,
237 Expr::Lit(ExprLit {
238 lit: Lit::Str(_),
239 ..
240 })
241 )
242 })
243 }
244
245 fn promote_to_list(&mut self, paren: token::Paren) -> Result<&mut MetaList1> {
246 let path = match self {
247 Meta1::Path(path) => path.clone(),
248 Meta1::List(metalist) => return Ok(metalist),
249 other => return Err(err_promote_to_list(other)),
250 };
251 *self = Meta1::List(MetaList1 {
252 path,
253 paren_token: paren,
254 nested: PunctuatedNestedMeta::new(),
255 });
256 self.list_mut()
257 }
258
259 fn list(&self) -> Result<&MetaList1> {
260 match self {
261 Meta1::List(ref list) => Ok(list),
262 other => Err(Error::new_spanned(other, "Not a List meta")),
263 }
264 }
265 fn list_mut(&mut self) -> Result<&mut MetaList1> {
266 match self {
267 Meta1::List(ref mut list) => Ok(list),
268 other => Err(Error::new_spanned(other, "Not a List meta")),
269 }
270 }
271
272 fn name_value(&self) -> Result<&MetaNameValue> {
273 match self {
274 Meta1::NameValue(ref name) => Ok(name),
275 other => Err(Error::new_spanned(other, "Not a NameValue meta")),
276 }
277 }
278 fn name_value_mut(&mut self) -> Result<&mut MetaNameValue> {
279 match self {
280 Meta1::NameValue(ref mut name) => Ok(name),
281 other => Err(Error::new_spanned(other, "Not a NameValue meta")),
282 }
283 }
284
285 fn doc(&self) -> Result<String> {
286 let name_value = self.name_value()?;
287 if !name_value.path.is_ident("doc") {
288 return Err(Error::new_spanned(name_value, "Not a doc meta"));
289 }
290 match &name_value.value {
291 Expr::Lit(ExprLit {
292 lit: Lit::Str(lit), ..
293 }) => Ok(lit.value().trim().to_owned()),
294 other => Err(Error::new_spanned(other, "Doc meta expects string literal")),
295 }
296 }
297}
298
299type IndexMetaRef<M> = (usize, M);
301type MultiMetaMap<'a, K, M> = Map<K, Vec<IndexMetaRef<M>>>;
302type UniqueMetaMap<'a, K, M> = Map<K, IndexMetaRef<M>>;
303
304pub trait MetaIteratorExt<'a, M>
306where
307 M: 'a + std::borrow::Borrow<Meta1>,
308{
309 fn to_multi_map<K, KF>(self, path_to_key: KF) -> Result<MultiMetaMap<'a, K, M>>
311 where
312 K: std::hash::Hash + Eq,
313 KF: Fn(&Path) -> Result<Option<K>>;
314
315 fn to_unique_map<K, KF>(self, path_to_key: KF) -> Result<UniqueMetaMap<'a, K, M>>
317 where
318 K: std::hash::Hash + Eq,
319 KF: Fn(&Path) -> Result<Option<K>>;
320}
321
322impl<'a, I, M> MetaIteratorExt<'a, M> for I
343where
344 M: 'a + std::borrow::Borrow<Meta1>,
345 I: std::iter::Iterator<Item = IndexMetaRef<M>>,
346{
347 fn to_multi_map<K, KF>(self, path_to_key: KF) -> Result<MultiMetaMap<'a, K, M>>
351 where
352 K: std::hash::Hash + Eq,
353 KF: Fn(&Path) -> Result<Option<K>>,
354 {
355 let mut map: Map<K, Vec<_>> = Map::new();
356 for (i, meta) in self {
357 let path = meta.borrow().path();
358 let key = if let Some(key) = path_to_key(path)? {
359 key
360 } else {
361 continue;
362 };
363 map.entry(key).or_default().push((i, meta))
364 }
365 Ok(map)
366 }
367
368 fn to_unique_map<K, KF>(self, path_to_key: KF) -> Result<UniqueMetaMap<'a, K, M>>
369 where
370 K: std::hash::Hash + Eq,
371 KF: Fn(&Path) -> Result<Option<K>>,
372 {
373 let mut map = Map::new();
374 for (i, meta) in self {
375 let path = meta.borrow().path();
376 let key = if let Some(key) = path_to_key(path)? {
377 key
378 } else {
379 continue;
380 };
381 if let Some((_, removed)) = map.insert(key, (i, meta)) {
382 return Err(Error::new_spanned(
383 removed.borrow(),
384 "this attribute path must be unique in the attribute",
385 ));
386 }
387 }
388 Ok(map)
389 }
390}
391
392#[allow(clippy::type_complexity)]
394pub trait NestedMetaRefIteratorExt<'a, M>
395where
396 M: 'a + std::borrow::Borrow<Meta1>,
397{
398 fn to_multi_map_and_lits<K, KF>(
410 self,
411 path_to_key: KF,
412 ) -> Result<(MultiMetaMap<'a, K, M>, Vec<(usize, &'a Lit)>)>
413 where
414 K: std::hash::Hash + Eq,
415 KF: Fn(&Path) -> Result<Option<K>>;
416
417 fn to_unique_map_and_lits<K, KF>(
418 self,
419 path_to_key: KF,
420 ) -> Result<(UniqueMetaMap<'a, K, M>, Vec<(usize, &'a Lit)>)>
421 where
422 K: std::hash::Hash + Eq,
423 KF: Fn(&Path) -> Result<Option<K>>;
424}
425
426#[allow(clippy::type_complexity)]
427impl<'a, I> NestedMetaRefIteratorExt<'a, &'a Meta1> for I
428where
429 I: std::iter::IntoIterator<Item = &'a NestedMeta>,
430{
431 fn to_multi_map_and_lits<K, KF>(
460 self,
461 path_to_key: KF,
462 ) -> Result<(MultiMetaMap<'a, K, &'a Meta1>, Vec<(usize, &'a Lit)>)>
463 where
464 K: std::hash::Hash + Eq,
465 KF: Fn(&Path) -> Result<Option<K>>,
466 {
467 let mut metas = Vec::new();
468 let mut lits = Vec::new();
469
470 for (i, nmeta) in self.into_iter().enumerate() {
471 match nmeta {
472 NestedMeta::Meta(meta) => metas.push((i, meta)),
473 NestedMeta::Lit(lit) => lits.push((i, lit)),
474 }
475 }
476
477 let map = metas.into_iter().to_multi_map(path_to_key)?;
478 Ok((map, lits))
479 }
480
481 fn to_unique_map_and_lits<K, KF>(
482 self,
483 path_to_key: KF,
484 ) -> Result<(UniqueMetaMap<'a, K, &'a Meta1>, Vec<(usize, &'a Lit)>)>
485 where
486 K: std::hash::Hash + Eq,
487 KF: Fn(&Path) -> Result<Option<K>>,
488 {
489 let mut metas = Vec::new();
490 let mut lits = Vec::new();
491
492 for (i, nmeta) in self.into_iter().enumerate() {
493 match nmeta {
494 NestedMeta::Meta(meta) => metas.push((i, meta)),
495 NestedMeta::Lit(lit) => lits.push((i, lit)),
496 }
497 }
498 let map = metas.into_iter().to_unique_map(path_to_key)?;
499 Ok((map, lits))
500 }
501}
502
503#[allow(clippy::type_complexity)]
505pub trait NestedMetaIteratorExt<'a> {
506 fn into_multi_map_and_lits<K, KF>(
507 self,
508 path_to_key: KF,
509 ) -> Result<(MultiMetaMap<'a, K, Meta1>, Vec<(usize, Lit)>)>
510 where
511 K: std::hash::Hash + Eq,
512 KF: Fn(&Path) -> Result<Option<K>>;
513
514 fn into_unique_map_and_lits<K, KF>(
515 self,
516 path_to_key: KF,
517 ) -> Result<(UniqueMetaMap<'a, K, Meta1>, Vec<(usize, Lit)>)>
518 where
519 K: std::hash::Hash + Eq,
520 KF: Fn(&Path) -> Result<Option<K>>;
521}
522
523#[allow(clippy::type_complexity)]
524impl<'a, I> NestedMetaIteratorExt<'a> for I
525where
526 I: std::iter::IntoIterator<Item = NestedMeta>,
527{
528 fn into_multi_map_and_lits<K, KF>(
529 self,
530 path_to_key: KF,
531 ) -> Result<(MultiMetaMap<'a, K, Meta1>, Vec<(usize, Lit)>)>
532 where
533 K: std::hash::Hash + Eq,
534 KF: Fn(&Path) -> Result<Option<K>>,
535 {
536 let mut metas = Vec::new();
537 let mut lits = Vec::new();
538
539 for (i, nmeta) in self.into_iter().enumerate() {
540 match nmeta {
541 NestedMeta::Meta(meta) => metas.push((i, meta)),
542 NestedMeta::Lit(lit) => lits.push((i, lit)),
543 }
544 }
545
546 let map = metas.into_iter().to_multi_map(path_to_key)?;
547 Ok((map, lits))
548 }
549
550 fn into_unique_map_and_lits<K, KF>(
551 self,
552 path_to_key: KF,
553 ) -> Result<(UniqueMetaMap<'a, K, Meta1>, Vec<(usize, Lit)>)>
554 where
555 K: std::hash::Hash + Eq,
556 KF: Fn(&Path) -> Result<Option<K>>,
557 {
558 let mut metas = Vec::new();
559 let mut lits = Vec::new();
560
561 for (i, nmeta) in self.into_iter().enumerate() {
562 match nmeta {
563 NestedMeta::Meta(meta) => metas.push((i, meta)),
564 NestedMeta::Lit(lit) => lits.push((i, lit)),
565 }
566 }
567 let map = metas.into_iter().to_unique_map(path_to_key)?;
568 Ok((map, lits))
569 }
570}
571
572#[allow(dead_code)]
574#[allow(clippy::type_complexity)]
575pub trait MetaAttributeExt<'a> {
576 fn to_multi_map_and_attrs<K, KF>(
577 self,
578 path_to_key: KF,
579 ) -> Result<(MultiMetaMap<'a, K, Meta1>, Vec<(usize, &'a Attribute)>)>
580 where
581 K: std::hash::Hash + Eq,
582 KF: Fn(&Path) -> Result<Option<K>>;
583
584 fn to_unique_map_and_attrs<K, KF>(
585 self,
586 path_to_key: KF,
587 ) -> Result<(UniqueMetaMap<'a, K, Meta1>, Vec<(usize, &'a Attribute)>)>
588 where
589 K: std::hash::Hash + Eq,
590 KF: Fn(&Path) -> Result<Option<K>>;
591}
592
593#[cfg(feature = "parsing")]
594#[allow(clippy::type_complexity)]
595impl<'a, I> MetaAttributeExt<'a> for I
596where
597 I: std::iter::IntoIterator<Item = &'a Attribute>,
598{
599 fn to_multi_map_and_attrs<K, KF>(
628 self,
629 path_to_key: KF,
630 ) -> Result<(MultiMetaMap<'a, K, Meta1>, Vec<(usize, &'a Attribute)>)>
631 where
632 K: std::hash::Hash + Eq,
633 KF: Fn(&Path) -> Result<Option<K>>,
634 {
635 let mut metas = Vec::new();
636 let mut attrs = Vec::new();
637
638 for (i, attr) in self.into_iter().enumerate() {
639 match attr.parse_meta() {
640 Ok(meta) => metas.push((i, meta)),
641 Err(_) => attrs.push((i, attr)),
642 }
643 }
644
645 let map = metas.into_iter().to_multi_map(path_to_key)?;
646 Ok((map, attrs))
647 }
648
649 fn to_unique_map_and_attrs<K, KF>(
650 self,
651 path_to_key: KF,
652 ) -> Result<(UniqueMetaMap<'a, K, Meta1>, Vec<(usize, &'a Attribute)>)>
653 where
654 K: std::hash::Hash + Eq,
655 KF: Fn(&Path) -> Result<Option<K>>,
656 {
657 let mut metas = Vec::new();
658 let mut attrs = Vec::new();
659
660 for (i, attr) in self.into_iter().enumerate() {
661 match attr.parse_meta() {
662 Ok(meta) => metas.push((i, meta)),
663 Err(_) => attrs.push((i, attr)),
664 }
665 }
666 let map = metas.into_iter().to_unique_map(path_to_key)?;
667 Ok((map, attrs))
668 }
669}
670
671impl GetPath for NestedMeta {
672 fn get_path(&self) -> Option<&Path> {
674 match self {
675 NestedMeta::Meta(meta) => Some(meta.path()),
676 NestedMeta::Lit(_) => None,
677 }
678 }
679}
680
681impl GetIdent for Meta1 {
682 fn get_ident(&self) -> Option<&Ident> {
684 self.path().get_ident()
685 }
686}