• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::ops::Deref;
2 
3 use syn::{Meta, Path};
4 
5 use crate::ast::NestedMeta;
6 use crate::{Error, FromMeta, Result};
7 
8 use super::path_to_string;
9 
10 /// A list of `syn::Path` instances. This type is used to extract a list of paths from an
11 /// attribute.
12 ///
13 /// # Usage
14 /// An `PathList` field on a struct implementing `FromMeta` will turn `#[builder(derive(serde::Debug, Clone))]` into:
15 ///
16 /// ```rust,ignore
17 /// StructOptions {
18 ///     derive: PathList(vec![syn::Path::new("serde::Debug"), syn::Path::new("Clone")])
19 /// }
20 /// ```
21 #[derive(Debug, Default, Clone, PartialEq, Eq)]
22 pub struct PathList(Vec<Path>);
23 
24 impl PathList {
25     /// Create a new list.
new<T: Into<Path>>(vals: Vec<T>) -> Self26     pub fn new<T: Into<Path>>(vals: Vec<T>) -> Self {
27         PathList(vals.into_iter().map(T::into).collect())
28     }
29 
30     /// Create a new `Vec` containing the string representation of each path.
to_strings(&self) -> Vec<String>31     pub fn to_strings(&self) -> Vec<String> {
32         self.0.iter().map(path_to_string).collect()
33     }
34 }
35 
36 impl Deref for PathList {
37     type Target = Vec<Path>;
38 
deref(&self) -> &Self::Target39     fn deref(&self) -> &Self::Target {
40         &self.0
41     }
42 }
43 
44 impl From<Vec<Path>> for PathList {
from(v: Vec<Path>) -> Self45     fn from(v: Vec<Path>) -> Self {
46         PathList(v)
47     }
48 }
49 
50 impl FromMeta for PathList {
from_list(v: &[NestedMeta]) -> Result<Self>51     fn from_list(v: &[NestedMeta]) -> Result<Self> {
52         let mut paths = Vec::with_capacity(v.len());
53         for nmi in v {
54             if let NestedMeta::Meta(Meta::Path(ref path)) = *nmi {
55                 paths.push(path.clone());
56             } else {
57                 return Err(Error::unexpected_type("non-word").with_span(nmi));
58             }
59         }
60 
61         Ok(PathList(paths))
62     }
63 }
64 
65 #[cfg(test)]
66 mod tests {
67     use super::PathList;
68     use crate::FromMeta;
69     use proc_macro2::TokenStream;
70     use quote::quote;
71     use syn::{parse_quote, Attribute, Meta};
72 
73     /// parse a string as a syn::Meta instance.
pm(tokens: TokenStream) -> ::std::result::Result<Meta, String>74     fn pm(tokens: TokenStream) -> ::std::result::Result<Meta, String> {
75         let attribute: Attribute = parse_quote!(#[#tokens]);
76         Ok(attribute.meta)
77     }
78 
fm<T: FromMeta>(tokens: TokenStream) -> T79     fn fm<T: FromMeta>(tokens: TokenStream) -> T {
80         FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
81             .expect("Tests should pass valid input")
82     }
83 
84     #[test]
succeeds()85     fn succeeds() {
86         let paths = fm::<PathList>(quote!(ignore(Debug, Clone, Eq)));
87         assert_eq!(
88             paths.to_strings(),
89             vec![
90                 String::from("Debug"),
91                 String::from("Clone"),
92                 String::from("Eq")
93             ]
94         );
95     }
96 
97     /// Check that the parser rejects non-word members of the list, and that the error
98     /// has an associated span.
99     #[test]
fails_non_word()100     fn fails_non_word() {
101         let input = PathList::from_meta(&pm(quote!(ignore(Debug, Clone = false))).unwrap());
102         let err = input.unwrap_err();
103         assert!(err.has_span());
104     }
105 }
106