attribute_derive/lib.rs
1#![warn(missing_docs)]
2#![allow(clippy::test_attr_in_doctest)]
3//! Basically clap for attribute macros:
4//! ```
5//! use attribute_derive::FromAttr;
6//! #[derive(FromAttr)]
7//! #[from_attr(ident = attr_name)]
8//! // overriding the builtin error messages
9//! #[from_attr(error(missing_field = "`{field}` was not specified"))]
10//! struct MyAttribute {
11//! // Positional values need to be specified before any named ones
12//! #[from_attr(positional)]
13//! positional: u8,
14//! // Options are optional by default (will be set to None if not specified)
15//! optional: Option<String>,
16//! required: String,
17//! // Any type implementing default can be flagged as default
18//! // This will be set to Vec::default() when not specified
19//! #[from_attr(optional)]
20//! list: Vec<syn::Type>,
21//! // Booleans can be used without assigning a value, i.e., as a flag.
22//! // If omitted they are set to false
23//! some_flag: bool,
24//! }
25//! ```
26//!
27//! Will be able to parse an attribute like this:
28//! ```rust
29//! # #[cfg(no)]
30//! #[attr_name(5, optional="some", required = r#"string"#, some_flag, list = [Option, ()])]
31//! // or
32//! #[attr_name(5, required = "string", list(Option, ()))]
33//! # struct Placeholder;
34//! ```
35//!
36//! Any type that for [`AttributeNamed`] or [`AttributePositional`] are
37//! implemented respectively are supported. These should be the general types
38//! that [`syn`] supports like [`LitStr`](struct@LitStr) or [`Type`] or that
39//! have a direct equivalent in those like [`String`], [`char`] or [`f32`]. A
40//! special treatment have [`Vecs`](Vec) which are parsed as either `name = [a,
41//! b, c]` or `name(a, b, c)` and [`Options`](Option) that will be [`None`] if
42//! not specified and [`Some`] when the value is specified via the attribute. It
43//! is not specified via `Some(value)` but as just `value`. [`Bools`](bool) are
44//! used for flags, i.e., without a value. Most should just behave as expected,
45//! see [`parsing`] for details.
46//!
47//! Tuple structs can derive [`FromAttr`] as well, but all fields will be
48//! positional. Tuples with a single field
49//! ([new types](https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html))
50//! will copy the behavior of the contained field, e.g. for [`bool`]:
51//!
52//! ```
53//! use syn::{Attribute, parse_quote};
54//! use attribute_derive::FromAttr;
55//!
56//! #[derive(FromAttr, PartialEq, Debug)]
57//! #[attribute(ident = flag)]
58//! struct Flag(bool);
59//!
60//! let attr: Attribute = parse_quote!(#[flag]);
61//! assert_eq!(Flag::from_attribute(attr).unwrap(), Flag(true));
62//!
63//! let attr: Attribute = parse_quote!(#[flag = true]);
64//! assert_eq!(Flag::from_attribute(attr).unwrap(), Flag(true));
65//!
66//! let attr: Attribute = parse_quote!(#[flag(false)]);
67//! assert_eq!(Flag::from_attribute(attr).unwrap(), Flag(false));
68//! ```
69//!
70//! # Attributes
71//!
72//! The parsing of attributes can be modified with the following parameters via
73//! the `#[attribute(<params>)]` attribute. All of them are optional. Error
74//! messages are formatted using [interpolator], and only support display and
75//! lists `i` formatting. See [interpolator] docs for details.
76//!
77//! ### Struct
78//!
79//! - `ident = <ident>` The attribute ident. Improves error messages and enables
80//! the [`from_attributes`](FromAttr::from_attributes) and
81//! [`remove_attributes`](FromAttr::remove_attributes) functions.
82//! - `aliases = [<alias>, ...]` Aliases for the attribute ident.
83//! - `error = "<error message>"` Overrides default error message.
84//! - `error(`
85//! - ``unknown_field = "supported fields are {expected_fields:i..-1(`{}`)(,
86//! )} and `{expected_fields:i-1}`",`` Custom error message printed if an
87//! unknown property is specified and attribute has more than one field.
88//! Placeholders: `{expected_fields:i}`.
89//! - ``unknown_field_single = "expected supported field
90//! `{expected_field}`",`` Custom error message printed if an unknown
91//! property is specified, and attribute only has a single field.
92//! Placeholders: `{expected_field}`.
93//! - ``unknown_field_empty = "expected empty attribute",`` Custom error
94//! message printed if a property is specified, and attribute has no
95//! fields.
96//! - ``duplicate_field = "`{field}` is specified multiple times",`` Custom
97//! error message printed if a property is specified multiple times.
98//! Placeholders: `{field}`.
99//! - ``missing_field = "required `{field}` is not specified",`` Custom
100//! error message printed if a required property is not specified.
101//! Placeholders: `{field}`.
102//! - ``field_help = "try `#[{attribute}({field}={example})]`",`` Additional
103//! help message printed if a required property is not specified or has an
104//! error. Placeholders: `{attribute}`, `{field}` and `{example}`.
105//! - ``conflict = "`{first}` conflicts with mutually exclusive
106//! `{second}`"`` Custom error message printed when conflicting properties
107//! are specified. Placeholders: `{first}` and `{second}`.
108//!
109//! `)`
110// //! - `duplicate = AggregateOrError` Change the behavior for duplicate arguments
111// //! (also across multiple attributes).
112// //! - `AggregateOrError` Aggregate multiple [`Vec`], error on everything else.
113// //! - `Error` Disables aggregation, errors on all duplicates.
114// //! - `AggregateOrOverride` Aggregate multiple [`Vec`], take the last
115// //! specified for everything else.
116// //! - `Override` Disables aggregation, always take the last value.
117//! ### Fields
118//!
119//! - `optional` If field is not specified, the default value is used instead.
120//! - `default = <default expr>` provides a default to be used instead of
121//! [`Default::default()`]. Enables `optional`.
122//! - `conflicts(<field>, ...)` Conflicting fields
123//! - `example = "<example>"`
124//!
125//! # Parse methods
126//!
127//! There are multiple ways of parsing a struct deriving [`FromAttr`].
128//!
129//! For helper attributes there is:
130//! - [`FromAttr::from_attributes`] which takes in an [`IntoIterator<Item = &'a
131//! syn::Attribute`](syn::Attribute) (e.g. a
132//! [`&Vec<syn::Attribute>`](syn::Attribute)). Most useful for derive macros.
133//! - [`FromAttr::remove_attributes`] which takes a [`&mut
134//! Vec<syn::Attribute>`](syn::Attribute) and does not only parse the
135//! attributes, but also removes those matching. Useful for helper attributes
136//! for attribute macros, where the helper attributes need to be removed.
137//!
138//! For parsing a single [`TokenStream`] e.g. for parsing the proc macro input
139//! there are two ways:
140//!
141//! - [`FromAttr::from_args`] taking in a [`TokenStream`]
142//! - As `derive(FromAttr)` also derives [`Parse`] so you can use the
143//! [parse](mod@syn::parse) API, e.g. with [`parse_macro_input!(tokens as
144//! Attribute)`](syn::parse_macro_input!).
145//!
146//! [interpolator]: https://docs.rs/interpolator/latest/interpolator/
147use std::borrow::Borrow;
148use std::fmt::Debug;
149use std::iter;
150
151#[doc(hidden)]
152pub use attribute_derive_macro::Attribute;
153pub use attribute_derive_macro::FromAttr;
154use manyhow::SpanRanged;
155#[cfg(doc)]
156use parsing::*;
157use parsing::{AttributeBase, SpannedValue};
158use proc_macro2::{Span, TokenStream};
159use syn::parse::{ParseStream, Parser, Result};
160#[cfg(doc)]
161use syn::{parse::Parse, LitStr, Type};
162use syn::{Error, Path};
163#[doc(hidden)]
164pub use tmp::FromAttr as Attribute;
165pub use tmp::FromAttr;
166
167#[doc(hidden)]
168pub mod __private {
169 pub use {proc_macro2, quote, syn};
170}
171
172mod std_impls;
173
174mod syn_impls;
175
176pub mod utils;
177pub use utils::FlagOrValue;
178
179pub mod parsing;
180
181pub mod from_partial;
182pub use from_partial::FromPartial;
183
184mod tmp {
185 use quote::ToTokens;
186 use syn::Meta;
187
188 use super::*;
189 /// The trait you actually derive on your attribute struct.
190 ///
191 /// Basic gist is a struct like this:
192 /// ```
193 /// # use attribute_derive::FromAttr;
194 /// # use syn::Type;
195 /// #[derive(FromAttr)]
196 /// #[attribute(ident = collection)]
197 /// #[attribute(error(missing_field = "`{field}` was not specified"))]
198 /// struct CollectionAttribute {
199 /// // Options are optional by default (will be set to None if not specified)
200 /// authority: Option<String>,
201 /// name: String,
202 /// // Any type implementing default can be flagged as optional
203 /// // This will be set to Vec::default() when not specified
204 /// #[attribute(optional)]
205 /// views: Vec<Type>,
206 /// // Booleans can be used without assiging a value. as a flag.
207 /// // If omitted they are set to false
208 /// some_flag: bool,
209 /// }
210 /// ```
211 ///
212 /// Will be able to parse an attribute like this:
213 /// ```text
214 /// #[collection(authority="Some String", name = r#"Another string"#, views = [Option, ()], some_flag)]
215 /// ```
216 pub trait FromAttr: Sized + AttributeBase {
217 /// Parses an [`IntoIterator`] of [`syn::Attributes`](syn::Attribute)
218 /// e.g. [`Vec<Attribute>`](Vec). Only available if you specify
219 /// the attribute ident: `#[attribute(ident="<ident>")]` when
220 /// using the derive macro.
221 ///
222 /// It can therefore parse fields set over multiple attributes like:
223 /// ```text
224 /// #[collection(authority = "Authority", name = "Name")]
225 /// #[collection(views = [A, B])]
226 /// ```
227 /// And also catch duplicate/conflicting settings over those.
228 ///
229 /// This is best used for derive macros, where you don't need to remove
230 /// your attributes.
231 ///
232 /// # Errors
233 /// Fails with a [`syn::Error`] so you can conveniently return that as a
234 /// compiler error in a proc macro in the following cases
235 ///
236 /// - A required parameter is omitted
237 /// - Invalid input is given for a parameter
238 /// - A non aggregating parameter is specified multiple times
239 /// - An attribute called [`IDENTS`](const@AttributeIdent::IDENTS) has
240 /// invalid syntax (e.g. `#attr(a: "a")`)
241 fn from_attributes<A: Borrow<syn::Attribute>>(
242 attrs: impl IntoIterator<Item = A>,
243 ) -> Result<Self>
244 where
245 Self: AttributeIdent,
246 {
247 attrs
248 .into_iter()
249 .filter(|attr| Self::is_ident(attr.borrow().path()))
250 .map(Self::from_attribute_partial)
251 .try_fold(None, |acc, item| {
252 Self::join(
253 acc,
254 SpannedValue::call_site(item?),
255 &format!("`{}` was specified twice", Self::ident()),
256 )
257 })
258 .and_then(|o| {
259 Self::from_option(
260 o.map(SpannedValue::value),
261 &format!("`{}` is not set", Self::ident()),
262 )
263 })
264 }
265
266 /// Parses a [`&mut Vec<syn::Attributes>`](syn::Attribute). Removing
267 /// matching attributes. Only available if you specify an ident:
268 /// `#[attribute(ident="<ident>")]` when using the derive macro.
269 ///
270 /// It can therefore parse fields set over multiple attributes like:
271 /// ```text
272 /// #[collection(authority = "Authority", name = "Name")]
273 /// #[collection(views = [A, B])]
274 /// ```
275 /// And also catch duplicate/conflicting settings over those.
276 ///
277 /// Use this if you are implementing an attribute macro, and need to
278 /// remove your helper attributes.
279 ///
280 /// ```
281 /// use syn::parse_quote;
282 /// use attribute_derive::FromAttr;
283 /// let mut attrs = vec![
284 /// parse_quote!(#[ignored]), parse_quote!(#[test]),
285 /// parse_quote!(#[also_ignored]), parse_quote!(#[test])
286 /// ];
287 /// #[derive(FromAttr)]
288 /// #[attribute(ident = test)]
289 /// struct Test {}
290 /// assert!(Test::remove_attributes(&mut attrs).is_ok());
291 ///
292 /// assert_eq!(attrs, vec![parse_quote!(#[ignored]), parse_quote!(#[also_ignored])]);
293 /// ```
294 ///
295 /// # Errors
296 /// Fails with a [`syn::Error`], so you can conveniently return that as
297 /// a compiler error in a proc macro in the following cases
298 ///
299 /// - A necessary parameter is omitted
300 /// - Invalid input is given for a parameter
301 /// - A non aggregating parameter is specified multiple times
302 /// - An attribute called [`IDENTS`](const@AttributeIdent::IDENTS) has
303 /// invalid syntax (e.g. `#attr(a: "a")`)
304 fn remove_attributes(attrs: &mut Vec<syn::Attribute>) -> Result<Self>
305 where
306 Self: AttributeIdent,
307 {
308 let mut i = 0;
309 Self::from_attributes(iter::from_fn(|| {
310 while i < attrs.len() {
311 if Self::is_ident(attrs[i].path()) {
312 return Some(attrs.remove(i));
313 }
314 i += 1;
315 }
316 None
317 }))
318 }
319
320 /// Parses from a single attribute. Ignoring the name.
321 ///
322 /// This is available even without `#[attribute(ident = ...)]`, because
323 /// it ignores the attribute's path, allowing to use it to parse e.g.
324 /// literals:
325 /// ```
326 /// use attribute_derive::FromAttr;
327 ///
328 /// let attr: syn::Attribute = syn::parse_quote!(#[test = "hello"]);
329 /// assert_eq!(String::from_attribute(attr).unwrap(), "hello");
330 ///
331 /// let attr: syn::Attribute = syn::parse_quote!(#[test]);
332 /// assert_eq!(bool::from_attribute(attr).unwrap(), true);
333 /// ```
334 fn from_attribute(attr: impl Borrow<syn::Attribute>) -> Result<Self> {
335 Self::from_attribute_partial(attr).and_then(Self::from)
336 }
337
338 #[doc(hidden)]
339 #[deprecated = "use `from_input` instead"]
340 fn from_args(tokens: TokenStream) -> Result<Self> {
341 Self::from_input(tokens)
342 }
343
344 /// Parses a [`TokenStream`](proc_macro2::TokenStream).
345 ///
346 /// Useful for implementing general proc macros to parse the input of
347 /// your macro.
348 ///
349 /// This is a convenience over [`parse_input`](Self::parse_input). More
350 /// details are documented there.
351 fn from_input(input: impl Into<TokenStream>) -> Result<Self> {
352 Self::parse_input.parse2(input.into())
353 }
354
355 /// Parses input as the complete attribute.
356 ///
357 /// Due to this only parsing the input for a single attribute it is not
358 /// able to aggregate input spread over multiple attributes.
359 ///
360 /// # Errors
361 /// Fails with a [`syn::Error`], so you can conveniently return that as
362 /// a compiler error in a proc macro in the following cases
363 ///
364 /// - A necessary parameter is omitted
365 /// - Invalid input is given for a parameter
366 /// - A non aggregating parameter is specified multiple times
367 fn parse_input(input: ParseStream) -> Result<Self> {
368 Self::parse_partial(input).and_then(Self::from)
369 }
370
371 /// Like [`parse_partial`](Self::parse_partial) but instead takes an
372 /// [`Attribute`](syn::Attribute).
373 ///
374 /// This allows it to support all three, `#[flag]`, `#[function(like)]`
375 /// and `#[name = value]` attributes.
376 fn from_attribute_partial(attr: impl Borrow<syn::Attribute>) -> Result<Self::Partial> {
377 let tokens = match attr.borrow().meta {
378 Meta::Path(_) => TokenStream::new(),
379 Meta::List(ref list) => list.tokens.clone(),
380 Meta::NameValue(ref nv) => nv.value.to_token_stream(),
381 };
382 Self::parse_partial.parse2(tokens)
383 }
384
385 /// Actual implementation for parsing the attribute. This is the only
386 /// function required to implement in this trait and derived by the
387 /// [`FromAttr`](macro@FromAttr) derive macro.
388 fn parse_partial(input: ParseStream) -> Result<Self::Partial>;
389 }
390}
391
392/// Helper trait providing the path for an attribute.
393///
394/// Automatically derived with [`FromAttr`], if `#[attribute(ident =
395/// "some_ident")]` is provided.
396pub trait AttributeIdent {
397 /// List of idents, must contain at least one ident.
398 const IDENTS: &'static [&'static str];
399
400 /// Tests if Attribute matches one of the idents.
401 fn is_ident(path: &Path) -> bool {
402 Self::IDENTS.iter().any(|ident| path.is_ident(ident))
403 }
404
405 /// Returns default ident.
406 ///
407 /// # Panics
408 /// The default implementation panics if `IDENTS` is empty. Implementors
409 /// should ensure this is not the case.
410 fn ident() -> &'static str {
411 Self::IDENTS
412 .first()
413 .expect("`AttributeIdent::IDENTS` should not be empty")
414 }
415}