1#![allow(dead_code)]
17
18use syn::punctuated::IntoIter;
19use syn::spanned::Spanned;
20use syn::{Attribute, DeriveInput, Ident, Lit, Meta, MetaNameValue, NestedMeta, Path, Result};
21
22macro_rules! attr_err {
28 ($attr:expr, $msg:tt) => {
29 attr_err!($attr.span(), NAME, $msg, EXAMPLE)
30 };
31 ($name:expr, $msg:tt, $example:tt) => {
32 attr_err!(::proc_macro2::Span::call_site(), $name, $msg, $example)
33 };
34 ($attr:expr, $name:expr, $msg:tt, $example:tt) => {
35 ::syn::Error::new(
36 $attr.span(),
37 format!("Attribute `#[{}]`: {}\nExample use: {}", $name, $msg, $example),
38 )
39 };
40}
41
42pub(crate) fn get_amplify_crate(input: &DeriveInput) -> Path {
43 let name = "amplify_crate";
44 let example = "#[amplify_crate(amplify_crate_path)]";
45 let default = Path::from(Ident::new("amplify", input.span()));
46
47 let list = match attr_list(&input.attrs, name, example).ok().unwrap_or(None) {
48 Some(x) => x,
49 None => return default,
50 };
51 nested_one_path(&list, name, example)
52 .ok()
53 .unwrap_or(None)
54 .unwrap_or(default)
55}
56
57pub fn attr_list<'a>(
58 attrs: impl IntoIterator<Item = &'a Attribute>,
59 ident: &str,
60 example: &str,
61) -> Result<Option<IntoIter<NestedMeta>>> {
62 for attr in attrs {
63 if attr.path.is_ident(ident) {
64 match attr.parse_meta() {
65 Ok(meta) => match meta {
66 Meta::Path(_) => {
67 return Err(attr_err!(ident, "unexpected path argument", example));
68 }
69 Meta::List(list) => return Ok(Some(list.nested.into_iter())),
70 Meta::NameValue(_) => {
71 return Err(attr_err!(
72 ident,
73 "unexpected `name=\"value\"` argument",
74 example
75 ));
76 }
77 },
78 Err(_) => return Err(attr_err!(ident, "wrong format", example)),
79 }
80 }
81 }
82
83 Ok(None)
84}
85
86pub fn attr_named_value<'a>(
87 attrs: impl IntoIterator<Item = &'a Attribute>,
88 ident: &str,
89 example: &str,
90) -> Result<Option<Lit>> {
91 for attr in attrs {
92 if attr.path.is_ident(ident) {
93 match attr.parse_meta() {
94 Ok(meta) => match meta {
95 Meta::Path(_) => {
96 return Err(attr_err!(ident, "unexpected path argument", example));
97 }
98 Meta::List(_) => {
99 return Err(attr_err!(
100 ident,
101 "must have form `name=\"value\"`, not `name(value)`",
102 example
103 ));
104 }
105 Meta::NameValue(name_val) => return Ok(Some(name_val.lit)),
106 },
107 Err(_) => return Err(attr_err!(ident, "wrong format", example)),
108 }
109 }
110 }
111
112 Ok(None)
113}
114
115pub fn nested_one_meta(
116 list: &IntoIter<NestedMeta>,
117 attr_name: &str,
118 example: &str,
119) -> Result<Option<Meta>> {
120 match list.len() {
121 0 => Err(attr_err!(attr_name, "unexpected absence of argument", example)),
122 1 => match list
123 .clone()
124 .peekable()
125 .peek()
126 .expect("Core library iterator is broken")
127 {
128 NestedMeta::Meta(meta) => Ok(Some(meta.clone())),
129 NestedMeta::Lit(_) => {
130 Err(attr_err!(attr_name, "unexpected literal for type identifier is met", example))
131 }
132 },
133 _ => Err(attr_err!(attr_name, "unexpected multiple type identifiers", example)),
134 }
135}
136
137pub fn nested_one_path(
138 list: &IntoIter<NestedMeta>,
139 attr_name: &str,
140 example: &str,
141) -> Result<Option<Path>> {
142 let meta = nested_one_meta(list, attr_name, example)?;
143 meta.map(|meta| match meta {
144 Meta::Path(path) => Ok(path),
145 _ => Err(attr_err!(attr_name, "unexpected attribute type", example)),
146 })
147 .transpose()
148}
149
150pub fn nested_one_named_value(
151 list: &IntoIter<NestedMeta>,
152 attr_name: &str,
153 example: &str,
154) -> Result<Option<MetaNameValue>> {
155 let meta = nested_one_meta(list, attr_name, example)?;
156 meta.map(|meta| match meta {
157 Meta::NameValue(nested_meta) => Ok(nested_meta),
158 _ => Err(attr_err!(attr_name, "unexpected attribute type", example)),
159 })
160 .transpose()
161}