1use proc_macro2::{Delimiter, Group, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
2use std::collections::BTreeSet as Set;
3
4use quote::{format_ident, quote, ToTokens};
5use syn::parse::discouraged::Speculative;
6use syn::parse::ParseStream;
7use syn::{
8 braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, Lifetime, LitFloat,
9 LitInt, LitStr, Meta, Result, Token,
10};
11
12#[derive(Default)]
13pub struct Attrs<'a> {
14 pub display: Option<Display<'a>>,
15 pub source: Option<&'a Attribute>,
16 pub from: Option<&'a Attribute>,
17 pub transparent: Option<Transparent<'a>>,
18 pub doc: Option<&'a Attribute>,
19}
20
21#[derive(Clone)]
22pub struct Display<'a> {
23 pub original: &'a Attribute,
24 pub fmt: LitStr,
25 pub args: TokenStream,
26 pub requires_fmt_machinery: bool,
27 pub has_bonus_display: bool,
28 pub implied_bounds: Set<(usize, Trait)>,
29 pub arg_tokens: Vec<Argument>,
30}
31
32impl<'a> Display<'a> {
33 pub fn recursing(&self) -> Option<TokenStream> {
34 for arg in &self.arg_tokens {
35 match arg {
36 _ => {}
47 };
48 }
49 None
50 }
51}
52
53#[derive(Copy, Clone)]
54pub struct Transparent<'a> {
55 pub original: &'a Attribute,
56 pub span: Span,
57}
58
59#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
60pub enum Trait {
61 Debug,
62 Display,
63 Octal,
64 LowerHex,
65 UpperHex,
66 Pointer,
67 Binary,
68 LowerExp,
69 UpperExp,
70}
71
72#[allow(dead_code)]
73#[derive(Clone)]
74pub enum Argument {
75 Ident(Ident),
76 Lifetime(Lifetime),
77 Literal(Literal),
78 Punct(Punct),
79}
80
81pub fn get(input: &[Attribute]) -> Result<Attrs> {
82 let mut attrs = Attrs::default();
83
84 for attr in input {
85 if attr.path().is_ident("error") && attr.path().is_ident("display") {
86 return Err(Error::new_spanned(
87 attr,
88 "cannot specifiy #[error] and #[display] attribute at the same time",
89 ));
90 } else if attr.path().is_ident("error") {
91 parse_display_attribute(&mut attrs, attr, "error")?;
92 } else if attr.path().is_ident("display") {
93 parse_display_attribute(&mut attrs, attr, "display")?;
94 } else if attr.path().is_ident("source") {
95 attr.meta.require_path_only()?;
96 if attrs.source.is_some() {
97 return Err(Error::new_spanned(attr, "duplicate #[source] attribute"));
98 }
99 attrs.source = Some(attr);
100 } else if attr.path().is_ident("from") {
101 match attr.meta {
102 Meta::Path(_) => {}
103 Meta::List(_) | Meta::NameValue(_) => {
104 continue;
106 }
107 }
108 if attrs.from.is_some() {
109 return Err(Error::new_spanned(attr, "duplicate #[from] attribute"));
110 }
111 attrs.from = Some(attr);
112 } else if attr.path().is_ident("doc") {
113 attrs.doc = Some(attr);
114 }
115 }
116
117 Ok(attrs)
118}
119
120fn parse_display_attribute<'a>(
121 attrs: &mut Attrs<'a>,
122 attr: &'a Attribute,
123 name: &'static str,
124) -> Result<()> {
125 syn::custom_keyword!(transparent);
126
127 attr.parse_args_with(|input: ParseStream| {
128 if let Some(kw) = input.parse::<Option<transparent>>()? {
129 if attrs.transparent.is_some() {
130 return Err(Error::new_spanned(
131 attr,
132 format!("duplicate #[{}(transparent)] attribute", name),
133 ));
134 }
135 attrs.transparent = Some(Transparent {
136 original: attr,
137 span: kw.span,
138 });
139 return Ok(());
140 }
141
142 let fmt: LitStr = input.parse()?;
143
144 let ahead = input.fork();
145 ahead.parse::<Option<Token![,]>>()?;
146 let mut arg_tokens = Vec::<Argument>::with_capacity(10);
147 let args = if ahead.is_empty() {
148 input.advance_to(&ahead);
149 TokenStream::new()
150 } else {
151 parse_token_expr(input, false, &mut arg_tokens)?
152 };
153
154 let requires_fmt_machinery = !args.is_empty();
155
156 let display = Display {
157 original: attr,
158 fmt,
159 args,
160 requires_fmt_machinery,
161 has_bonus_display: false,
162 implied_bounds: Set::new(),
163 arg_tokens,
164 };
165 if attrs.display.is_some() {
166 return Err(Error::new_spanned(
167 attr,
168 format!("only one #[{}(...)] attribute is allowed", name),
169 ));
170 }
171 attrs.display = Some(display);
172 Ok(())
173 })
174}
175
176fn parse_token_expr(
177 input: ParseStream,
178 mut begin_expr: bool,
179 arg_tokens: &mut Vec<Argument>,
180) -> Result<TokenStream> {
181 let mut tokens = Vec::new();
182 while !input.is_empty() {
183 if let Some((ident, _)) = input.cursor().ident() {
184 arg_tokens.push(Argument::Ident(ident));
185 } else if let Some((lifetime, _)) = input.cursor().lifetime() {
186 arg_tokens.push(Argument::Lifetime(lifetime));
187 } else if let Some((literal, _)) = input.cursor().literal() {
188 arg_tokens.push(Argument::Literal(literal));
189 } else if let Some((punct, _)) = input.cursor().punct() {
190 arg_tokens.push(Argument::Punct(punct));
191 }
192
193 if begin_expr && input.peek(Token![.]) {
194 if input.peek2(Ident) {
195 input.parse::<Token![.]>()?;
196 begin_expr = false;
197 continue;
198 } else if input.peek2(LitInt) {
199 input.parse::<Token![.]>()?;
200 let int: Index = input.parse()?;
201 tokens.push({
202 let ident = format_ident!("_{}", int.index, span = int.span);
203 TokenTree::Ident(ident)
204 });
205 begin_expr = false;
206 continue;
207 } else if input.peek2(LitFloat) {
208 let ahead = input.fork();
209 ahead.parse::<Token![.]>()?;
210 let float: LitFloat = ahead.parse()?;
211 let repr = float.to_string();
212 let mut indices = repr.split('.').map(syn::parse_str::<Index>);
213 if let (Some(Ok(first)), Some(Ok(second)), None) =
214 (indices.next(), indices.next(), indices.next())
215 {
216 input.advance_to(&ahead);
217 tokens.push({
218 let ident = format_ident!("_{}", first, span = float.span());
219 TokenTree::Ident(ident)
220 });
221 tokens.push({
222 let mut punct = Punct::new('.', Spacing::Alone);
223 punct.set_span(float.span());
224 TokenTree::Punct(punct)
225 });
226 tokens.push({
227 let mut literal = Literal::u32_unsuffixed(second.index);
228 literal.set_span(float.span());
229 TokenTree::Literal(literal)
230 });
231 begin_expr = false;
232 continue;
233 }
234 }
235 }
236
237 begin_expr = input.peek(Token![break])
238 || input.peek(Token![continue])
239 || input.peek(Token![if])
240 || input.peek(Token![in])
241 || input.peek(Token![match])
242 || input.peek(Token![mut])
243 || input.peek(Token![return])
244 || input.peek(Token![while])
245 || input.peek(Token![+])
246 || input.peek(Token![&])
247 || input.peek(Token![!])
248 || input.peek(Token![^])
249 || input.peek(Token![,])
250 || input.peek(Token![/])
251 || input.peek(Token![=])
252 || input.peek(Token![>])
253 || input.peek(Token![<])
254 || input.peek(Token![|])
255 || input.peek(Token![%])
256 || input.peek(Token![;])
257 || input.peek(Token![*])
258 || input.peek(Token![-]);
259
260 let token: TokenTree = if input.peek(token::Paren) {
261 let content;
262 let delimiter = parenthesized!(content in input);
263 let nested = parse_token_expr(&content, true, arg_tokens)?;
264 let mut group = Group::new(Delimiter::Parenthesis, nested);
265 group.set_span(delimiter.span.join());
266 TokenTree::Group(group)
267 } else if input.peek(token::Brace) {
268 let content;
269 let delimiter = braced!(content in input);
270 let nested = parse_token_expr(&content, true, arg_tokens)?;
271 let mut group = Group::new(Delimiter::Brace, nested);
272 group.set_span(delimiter.span.join());
273 TokenTree::Group(group)
274 } else if input.peek(token::Bracket) {
275 let content;
276 let delimiter = bracketed!(content in input);
277 let nested = parse_token_expr(&content, true, arg_tokens)?;
278 let mut group = Group::new(Delimiter::Bracket, nested);
279 group.set_span(delimiter.span.join());
280 TokenTree::Group(group)
281 } else {
282 input.parse()?
283 };
284 tokens.push(token);
285 }
286 Ok(TokenStream::from_iter(tokens))
287}
288
289impl ToTokens for Display<'_> {
290 fn to_tokens(&self, tokens: &mut TokenStream) {
291 let fmt = &self.fmt;
292 let args = &self.args;
293
294 tokens.extend(if self.requires_fmt_machinery {
298 quote! {
299 ::core::write!(__formatter, #fmt #args)
300 }
301 } else {
302 quote! {
303 __formatter.write_str(#fmt)
304 }
305 });
306 }
307}
308
309impl ToTokens for Trait {
310 fn to_tokens(&self, tokens: &mut TokenStream) {
311 let trait_name = match self {
312 Trait::Debug => "Debug",
313 Trait::Display => "Display",
314 Trait::Octal => "Octal",
315 Trait::LowerHex => "LowerHex",
316 Trait::UpperHex => "UpperHex",
317 Trait::Pointer => "Pointer",
318 Trait::Binary => "Binary",
319 Trait::LowerExp => "LowerExp",
320 Trait::UpperExp => "UpperExp",
321 };
322 let ident = Ident::new(trait_name, Span::call_site());
323 tokens.extend(quote!(::core::fmt::#ident));
324 }
325}