ion_proc/attribute/
mod.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use proc_macro2::{Ident, TokenStream};
8use quote::quote;
9use syn::meta::ParseNestedMeta;
10use syn::parse::Parse;
11use syn::punctuated::Punctuated;
12use syn::{Attribute, Meta, MetaList, Path, Result, Token, bracketed};
13
14pub(crate) mod class;
15pub(crate) mod function;
16pub(crate) mod krate;
17pub(crate) mod name;
18pub(crate) mod property;
19pub(crate) mod trace;
20pub(crate) mod value;
21
22#[derive(Copy, Clone, Debug)]
23pub(crate) struct Optional<T>(pub(crate) Option<T>);
24
25impl<T> Default for Optional<T> {
26	fn default() -> Optional<T> {
27		Optional(None)
28	}
29}
30
31pub(crate) enum ArgumentError<'a> {
32	Kind(&'a str),
33	Full(&'a str),
34	None,
35}
36
37impl ArgumentError<'_> {
38	fn error(self, meta: &ParseNestedMeta, key: &str) -> Result<()> {
39		match self {
40			ArgumentError::Kind(kind) => Err(meta.error(format!("{kind} cannot have multiple `{key}` attributes."))),
41			ArgumentError::Full(error) => Err(meta.error(error)),
42			ArgumentError::None => Ok(()),
43		}
44	}
45}
46
47impl<'a> From<&'a str> for ArgumentError<'a> {
48	fn from(kind: &'a str) -> ArgumentError<'a> {
49		ArgumentError::Kind(kind)
50	}
51}
52
53impl<'a> From<Option<&'a str>> for ArgumentError<'a> {
54	fn from(kind: Option<&'a str>) -> ArgumentError<'a> {
55		match kind {
56			Some(kind) => ArgumentError::from(kind),
57			None => ArgumentError::None,
58		}
59	}
60}
61
62pub(crate) trait ParseArgumentWith {
63	type With;
64
65	fn handle_argument_with<'a>(
66		&mut self, meta: &ParseNestedMeta, with: Self::With, key: &str, error: impl Into<ArgumentError<'a>>,
67	) -> Result<bool>;
68
69	fn parse_argument_with<'a>(
70		&mut self, meta: &ParseNestedMeta, with: Self::With, key: &str, error: impl Into<ArgumentError<'a>>,
71	) -> Result<bool> {
72		if meta.path.is_ident(key) {
73			return self.handle_argument_with(meta, with, key, error);
74		}
75		Ok(false)
76	}
77}
78
79pub(crate) trait ParseArgument: ParseArgumentWith {
80	fn handle_argument<'a>(
81		&mut self, meta: &ParseNestedMeta, key: &str, error: impl Into<ArgumentError<'a>>,
82	) -> Result<bool>;
83
84	fn parse_argument<'a>(
85		&mut self, meta: &ParseNestedMeta, key: &str, error: impl Into<ArgumentError<'a>>,
86	) -> Result<bool> {
87		if meta.path.is_ident(key) {
88			return self.handle_argument(meta, key, error);
89		}
90		Ok(false)
91	}
92}
93
94impl ParseArgumentWith for bool {
95	type With = bool;
96
97	fn handle_argument_with<'a>(
98		&mut self, meta: &ParseNestedMeta, with: bool, key: &str, error: impl Into<ArgumentError<'a>>,
99	) -> Result<bool> {
100		if *self {
101			error.into().error(meta, key)?;
102			return Ok(false);
103		}
104		*self = with;
105		Ok(true)
106	}
107}
108
109impl ParseArgument for bool {
110	fn handle_argument<'a>(
111		&mut self, meta: &ParseNestedMeta, key: &str, error: impl Into<ArgumentError<'a>>,
112	) -> Result<bool> {
113		self.handle_argument_with(meta, true, key, error)
114	}
115}
116
117impl<T> ParseArgumentWith for Option<T> {
118	type With = T;
119
120	fn handle_argument_with<'a>(
121		&mut self, meta: &ParseNestedMeta, with: T, key: &str, error: impl Into<ArgumentError<'a>>,
122	) -> Result<bool> {
123		if self.is_some() {
124			error.into().error(meta, key)?;
125			return Ok(false);
126		}
127		*self = Some(with);
128		Ok(true)
129	}
130}
131
132impl<T: Parse> ParseArgument for Option<T> {
133	fn handle_argument<'a>(
134		&mut self, meta: &ParseNestedMeta, key: &str, error: impl Into<ArgumentError<'a>>,
135	) -> Result<bool> {
136		let _: Token![=] = meta.input.parse()?;
137		let argument = meta.input.parse()?;
138		self.handle_argument_with(meta, argument, key, error)
139	}
140}
141
142impl<T> ParseArgumentWith for Optional<T> {
143	type With = T;
144
145	fn handle_argument_with<'a>(
146		&mut self, meta: &ParseNestedMeta, with: T, key: &str, error: impl Into<ArgumentError<'a>>,
147	) -> Result<bool> {
148		if self.0.is_some() {
149			error.into().error(meta, key)?;
150			return Ok(false);
151		}
152		self.0 = Some(with);
153		Ok(true)
154	}
155}
156
157impl<T: Parse + Default> ParseArgument for Optional<T> {
158	fn handle_argument<'a>(
159		&mut self, meta: &ParseNestedMeta, key: &str, error: impl Into<ArgumentError<'a>>,
160	) -> Result<bool> {
161		let eq: Option<Token![=]> = meta.input.parse()?;
162		let argument = eq.map(|_| meta.input.parse()).transpose()?.unwrap_or_default();
163		self.handle_argument_with(meta, argument, key, error)
164	}
165}
166
167impl<T> ParseArgumentWith for Vec<T> {
168	type With = Punctuated<T, Token![,]>;
169
170	fn handle_argument_with<'a>(
171		&mut self, meta: &ParseNestedMeta, with: Punctuated<T, Token![,]>, key: &str,
172		error: impl Into<ArgumentError<'a>>,
173	) -> Result<bool> {
174		if !self.is_empty() {
175			error.into().error(meta, key)?;
176			return Ok(false);
177		}
178		self.extend(with);
179		Ok(true)
180	}
181}
182
183impl<T: Parse> ParseArgument for Vec<T> {
184	fn handle_argument<'a>(
185		&mut self, meta: &ParseNestedMeta, key: &str, error: impl Into<ArgumentError<'a>>,
186	) -> Result<bool> {
187		let _: Token![=] = meta.input.parse()?;
188		let inner;
189		bracketed!(inner in meta.input);
190		let value = inner.parse_terminated(T::parse, Token![,])?;
191		self.handle_argument_with(meta, value, key, error)
192	}
193}
194
195pub(crate) trait ParseAttribute: Default {
196	type Parent<'a>: Copy;
197
198	fn from_parent(_parent: Self::Parent<'_>) -> Self {
199		Self::default()
200	}
201
202	fn merge(&mut self, _parent: Self::Parent<'_>) {}
203
204	fn parse(&mut self, meta: &ParseNestedMeta) -> Result<bool>;
205
206	fn from_attributes<I: ?Sized>(path: &I, attributes: &mut Vec<Attribute>, parent: Self::Parent<'_>) -> Result<Self>
207	where
208		Ident: PartialEq<I>,
209	{
210		let mut data = Self::from_parent(parent);
211
212		attributes.retain_mut(|attribute| {
213			if !attribute.path().is_ident(path) {
214				return true;
215			}
216
217			let mut raw: Vec<(Path, TokenStream)> = Vec::new();
218			let mut parsed = Vec::new();
219
220			let _ = attribute.parse_nested_meta(|meta| {
221				raw.push((meta.path.clone(), meta.input.fork().parse()?));
222				if data.parse(&meta)? {
223					parsed.push(raw.len());
224				}
225
226				Ok(())
227			});
228
229			if parsed.len() == raw.len() {
230				return false;
231			}
232
233			if let Meta::List(MetaList { tokens, .. }) = &mut attribute.meta {
234				let raw = raw
235					.into_iter()
236					.enumerate()
237					.filter_map(|(i, (k, v))| (!parsed.contains(&i)).then(|| quote!(#k #v)));
238				*tokens = quote!(#(#raw),*);
239			}
240
241			true
242		});
243
244		data.merge(parent);
245		Ok(data)
246	}
247}