syn_ext/
meta.rs

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
181/// Shortcut type for [syn::MetaList::nested]
182pub type PunctuatedNestedMeta = Punctuated<NestedMeta, token::Comma>;
183
184/// Extension for [syn::Meta]
185pub trait MetaExt {
186    /// Returns `true` if self matches [syn::Meta::Path]
187    fn is_path(&self) -> bool;
188    /// Returns `true` if self matches [syn::Meta::List]
189    fn is_list(&self) -> bool;
190    /// Returns `true` if self matches [syn::Meta::NameValue]
191    fn is_name_value(&self) -> bool;
192    /// Returns `true` if the content matches `doc = <string lit>`
193    fn is_doc(&self) -> bool;
194
195    /// Promotes to empty [syn::Meta::List] with given `paren` if [syn::Meta::Path]
196    ///
197    /// A [syn::Meta::Path] value can be regarded as an empty [syn::Meta::List].
198    /// `promote` means converting [syn::Meta::Path] to an actual empty [syn::Meta::List].
199    fn promote_to_list(&mut self, paren: token::Paren) -> Result<&mut MetaList1>;
200
201    /// Returns [syn::MetaList] of [syn::Meta::List]; Otherwise `Err`
202    fn list(&self) -> Result<&MetaList1>;
203    /// Returns [syn::MetaList] of [syn::Meta::List]; Otherwise `Err`
204    fn list_mut(&mut self) -> Result<&mut MetaList1>;
205
206    /// Returns [syn::MetaNameValue] of [syn::Meta::NameValue]; Otherwise `Err`
207    fn name_value(&self) -> Result<&MetaNameValue>;
208    /// Returns [syn::MetaNameValue] of [syn::Meta::NameValue]; Otherwise `Err`
209    fn name_value_mut(&mut self) -> Result<&mut MetaNameValue>;
210
211    /// Returns content of `doc = <content>`
212    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
299// where  M: 'a + std::borrow::Borrow<Meta>
300type IndexMetaRef<M> = (usize, M);
301type MultiMetaMap<'a, K, M> = Map<K, Vec<IndexMetaRef<M>>>;
302type UniqueMetaMap<'a, K, M> = Map<K, IndexMetaRef<M>>;
303
304/// Constructs and returns map from [syn::Meta] iterator
305pub trait MetaIteratorExt<'a, M>
306where
307    M: 'a + std::borrow::Borrow<Meta1>,
308{
309    /// Constructs and returns a multi-value map from [syn::Meta] iterator
310    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    /// Constructs and returns a unique-value map from [syn::Meta] iterator. `Err` if duplicates.
316    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
322// fn to_multi_map<'a, I, M, K, KF>(iter: I, path_to_key: KF) -> Result<MultiMetaMap<'a, K, M>>
323// where
324//     M: 'a + std::borrow::Borrow<Meta>,
325//     I: std::iter::Iterator<Item = IndexMetaRef<M>>,
326//     K: std::hash::Hash + Eq,
327//     KF: Fn(&Path) -> Result<Option<K>>,
328// {
329//     let mut map: Map<K, Vec<_>> = Map::new();
330//     for (i, meta) in iter {
331//         let path = meta.borrow().path();
332//         let key = if let Some(key) = path_to_key(path)? {
333//             key
334//         } else {
335//             continue;
336//         };
337//         map.entry(key).or_default().push((i, meta))
338//     }
339//     Ok(map)
340// }
341
342impl<'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    // easier KF with traits?
348    // KF: Err(_) for error, Ok(None) to skip, Ok(Some(_)) to push
349
350    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/// experimental
393#[allow(clippy::type_complexity)]
394pub trait NestedMetaRefIteratorExt<'a, M>
395where
396    M: 'a + std::borrow::Borrow<Meta1>,
397{
398    // fn to_map_and_lits<K, KF, MT, MF, MI>(
399    //     &'a self,
400    //     path_to_key: KF,
401    //     mata_to_map: MF,
402    // ) -> Result<(MT, Vec<(usize, &'a Lit)>)>
403    // where
404    //     K: std::hash::Hash + Eq,
405    //     KF: Fn(&Path) -> Result<Option<K>>,
406    //     MF: Fn(MI, KF) -> Result<MT>,
407    //     MI: std::iter::Iterator<Item = (usize, &'a Meta)>;
408
409    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    // easier KF with traits?
432    // KF: Err(_) for error, Ok(None) to skip, Ok(Some(_)) to push
433
434    // fn to_map_and_lits<K, KF, MT, MF, MI>(
435    //     &'a self,
436    //     path_to_key: KF,
437    //     mata_iter_to_map: MF,
438    // ) -> Result<(MT, Vec<(usize, &'a Lit)>)>
439    // where
440    //     K: std::hash::Hash + Eq,
441    //     KF: Fn(&Path) -> Result<Option<K>>,
442    //     MF: Fn(MI, KF) -> Result<MT>,
443    //     MI: std::iter::Iterator<Item = (usize, &'a Meta)>,
444    // {
445    //     let mut metas: Vec<(_, &'a _)> = Vec::new();
446    //     let mut lits = Vec::new();
447
448    //     for (i, nmeta) in self.iter().enumerate() {
449    //         match nmeta {
450    //             NestedMeta::Meta(meta) => metas.push((i, meta)),
451    //             NestedMeta::Lit(lit) => lits.push((i, lit)),
452    //         }
453    //     }
454
455    //     let map = mata_iter_to_map(metas.into_iter(), path_to_key)?;
456    //     Ok((map, lits))
457    // }
458
459    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/// experimental
504#[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/// experimental
573#[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    // easier KF with traits?
600    // KF: Err(_) for error, Ok(None) to skip, Ok(Some(_)) to push
601
602    // fn to_map_and_attrs<K, KF, MT, MF, MI>(
603    //     &'a self,
604    //     path_to_key: KF,
605    //     mata_iter_to_map: MF,
606    // ) -> Result<(MT, Vec<(usize, &'a Attribute)>)>
607    // where
608    //     K: std::hash::Hash + Eq,
609    //     KF: Fn(&Path) -> Result<Option<K>>,
610    //     MF: Fn(MI, KF) -> Result<MT>,
611    //     MI: std::iter::Iterator<Item = IndexMetaRef<Meta>>,
612    // {
613    //     let mut metas = Vec::new();
614    //     let mut attrs = Vec::new();
615
616    //     for (i, attr) in self.iter().enumerate() {
617    //         match attr.parse_meta() {
618    //             Ok(meta) => metas.push((i, meta)),
619    //             Err(_) => attrs.push((i, attr)),
620    //         }
621    //     }
622
623    //     let map = mata_iter_to_map(metas.into_iter(), path_to_key)?;
624    //     Ok((map, attrs))
625    // }
626
627    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    /// Get path if [syn::NestedMeta::Meta]; Otherwise `None`
673    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    /// Get ident of [syn::Meta::path]
683    fn get_ident(&self) -> Option<&Ident> {
684        self.path().get_ident()
685    }
686}