1use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
2use quote::{format_ident, quote, ToTokens};
3use std::collections::BTreeSet as Set;
4use syn::parse::ParseStream;
5use syn::{
6 braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitInt, LitStr, Meta,
7 Result, Token,
8};
9
10pub struct Attrs<'a> {
11 pub display: Option<Display<'a>>,
12 pub source: Option<&'a Attribute>,
13 pub backtrace: Option<&'a Attribute>,
14 pub from: Option<&'a Attribute>,
15 pub transparent: Option<Transparent<'a>>,
16}
17
18#[derive(Clone)]
19pub struct Display<'a> {
20 pub original: &'a Attribute,
21 pub fmt: LitStr,
22 pub args: TokenStream,
23 pub has_bonus_display: bool,
24 pub implied_bounds: Set<(usize, Trait)>,
25}
26
27#[derive(Copy, Clone)]
28pub struct Transparent<'a> {
29 pub original: &'a Attribute,
30 pub span: Span,
31}
32
33#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
34pub enum Trait {
35 Debug,
36 Display,
37 Octal,
38 LowerHex,
39 UpperHex,
40 Pointer,
41 Binary,
42 LowerExp,
43 UpperExp,
44}
45
46pub fn get(input: &[Attribute]) -> Result<Attrs> {
47 let mut attrs = Attrs {
48 display: None,
49 source: None,
50 backtrace: None,
51 from: None,
52 transparent: None,
53 };
54
55 for attr in input {
56 if attr.path().is_ident("error") {
57 parse_error_attribute(&mut attrs, attr)?;
58 } else if attr.path().is_ident("source") {
59 attr.meta.require_path_only()?;
60 if attrs.source.is_some() {
61 return Err(Error::new_spanned(attr, "duplicate #[source] attribute"));
62 }
63 attrs.source = Some(attr);
64 } else if attr.path().is_ident("backtrace") {
65 attr.meta.require_path_only()?;
66 if attrs.backtrace.is_some() {
67 return Err(Error::new_spanned(attr, "duplicate #[backtrace] attribute"));
68 }
69 attrs.backtrace = Some(attr);
70 } else if attr.path().is_ident("from") {
71 match attr.meta {
72 Meta::Path(_) => {}
73 Meta::List(_) | Meta::NameValue(_) => {
74 // Assume this is meant for derive_more crate or something.
75 continue;
76 }
77 }
78 if attrs.from.is_some() {
79 return Err(Error::new_spanned(attr, "duplicate #[from] attribute"));
80 }
81 attrs.from = Some(attr);
82 }
83 }
84
85 Ok(attrs)
86}
87
88fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Result<()> {
89 syn::custom_keyword!(transparent);
90
91 attr.parse_args_with(|input: ParseStream| {
92 if let Some(kw) = input.parse::<Option<transparent>>()? {
93 if attrs.transparent.is_some() {
94 return Err(Error::new_spanned(
95 attr,
96 "duplicate #[error(transparent)] attribute",
97 ));
98 }
99 attrs.transparent = Some(Transparent {
100 original: attr,
101 span: kw.span,
102 });
103 return Ok(());
104 }
105
106 let display = Display {
107 original: attr,
108 fmt: input.parse()?,
109 args: parse_token_expr(input, false)?,
110 has_bonus_display: false,
111 implied_bounds: Set::new(),
112 };
113 if attrs.display.is_some() {
114 return Err(Error::new_spanned(
115 attr,
116 "only one #[error(...)] attribute is allowed",
117 ));
118 }
119 attrs.display = Some(display);
120 Ok(())
121 })
122}
123
124fn parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStream> {
125 let mut tokens = Vec::new();
126 while !input.is_empty() {
127 if begin_expr && input.peek(Token![.]) {
128 if input.peek2(Ident) {
129 input.parse::<Token![.]>()?;
130 begin_expr = false;
131 continue;
132 }
133 if input.peek2(LitInt) {
134 input.parse::<Token![.]>()?;
135 let int: Index = input.parse()?;
136 let ident = format_ident!("_{}", int.index, span = int.span);
137 tokens.push(TokenTree::Ident(ident));
138 begin_expr = false;
139 continue;
140 }
141 }
142
143 begin_expr = input.peek(Token![break])
144 || input.peek(Token![continue])
145 || input.peek(Token![if])
146 || input.peek(Token![in])
147 || input.peek(Token![match])
148 || input.peek(Token![mut])
149 || input.peek(Token![return])
150 || input.peek(Token![while])
151 || input.peek(Token![+])
152 || input.peek(Token![&])
153 || input.peek(Token![!])
154 || input.peek(Token![^])
155 || input.peek(Token![,])
156 || input.peek(Token![/])
157 || input.peek(Token![=])
158 || input.peek(Token![>])
159 || input.peek(Token![<])
160 || input.peek(Token![|])
161 || input.peek(Token![%])
162 || input.peek(Token![;])
163 || input.peek(Token![*])
164 || input.peek(Token![-]);
165
166 let token: TokenTree = if input.peek(token::Paren) {
167 let content;
168 let delimiter = parenthesized!(content in input);
169 let nested = parse_token_expr(&content, true)?;
170 let mut group = Group::new(Delimiter::Parenthesis, nested);
171 group.set_span(delimiter.span.join());
172 TokenTree::Group(group)
173 } else if input.peek(token::Brace) {
174 let content;
175 let delimiter = braced!(content in input);
176 let nested = parse_token_expr(&content, true)?;
177 let mut group = Group::new(Delimiter::Brace, nested);
178 group.set_span(delimiter.span.join());
179 TokenTree::Group(group)
180 } else if input.peek(token::Bracket) {
181 let content;
182 let delimiter = bracketed!(content in input);
183 let nested = parse_token_expr(&content, true)?;
184 let mut group = Group::new(Delimiter::Bracket, nested);
185 group.set_span(delimiter.span.join());
186 TokenTree::Group(group)
187 } else {
188 input.parse()?
189 };
190 tokens.push(token);
191 }
192 Ok(TokenStream::from_iter(tokens))
193}
194
195impl ToTokens for Display<'_> {
196 fn to_tokens(&self, tokens: &mut TokenStream) {
197 let fmt: &LitStr = &self.fmt;
198 let args: &TokenStream = &self.args;
199 tokens.extend(iter:quote! {
200 std::write!(__formatter, #fmt #args)
201 });
202 }
203}
204
205impl ToTokens for Trait {
206 fn to_tokens(&self, tokens: &mut TokenStream) {
207 let trait_name: Ident = format_ident!("{}", format!("{:?}", self));
208 tokens.extend(iter:quote!(std::fmt::#trait_name));
209 }
210}
211