1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
use proc_macro2::{TokenStream, TokenTree}; use quote::{format_ident, quote, ToTokens}; use std::iter::once; use syn::parse::{Nothing, Parse, ParseStream}; use syn::{Attribute, Error, Field, Ident, Index, LitInt, LitStr, Result, Token}; pub struct Display { pub fmt: LitStr, pub args: TokenStream, } impl Parse for Display { fn parse(input: ParseStream) -> Result<Self> { let fmt: LitStr = input.parse()?; let mut args = TokenStream::new(); let mut last_is_comma = false; while !input.is_empty() { if last_is_comma && input.peek(Token![.]) { if input.peek2(Ident) { input.parse::<Token![.]>()?; last_is_comma = false; continue; } if input.peek2(LitInt) { input.parse::<Token![.]>()?; let int: Index = input.parse()?; let ident = format_ident!("_{}", int.index, span = int.span); args.extend(once(TokenTree::Ident(ident))); last_is_comma = false; continue; } } last_is_comma = input.peek(Token![,]); let token: TokenTree = input.parse()?; args.extend(once(token)); } let mut display = Display { fmt, args }; display.expand_shorthand(); Ok(display) } } impl ToTokens for Display { fn to_tokens(&self, tokens: &mut TokenStream) { let fmt = &self.fmt; let args = &self.args; tokens.extend(quote! { write!(formatter, #fmt #args) }); } } pub fn is_source(field: &Field) -> Result<bool> { for attr in &field.attrs { if attr.path.is_ident("source") { syn::parse2::<Nothing>(attr.tokens.clone())?; return Ok(true); } } Ok(false) } pub fn display(attrs: &[Attribute]) -> Result<Option<Display>> { let mut display = None; for attr in attrs { if attr.path.is_ident("error") { if display.is_some() { return Err(Error::new_spanned( attr, "only one #[error(...)] attribute is allowed", )); } display = Some(attr.parse_args()?); } } Ok(display) }