Skip to main content

displaydoc/
attr.rs

1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use syn::{Attribute, LitStr, Meta, Result};
4
5#[derive(#[automatically_derived]
impl ::core::clone::Clone for Display {
    #[inline]
    fn clone(&self) -> Display {
        Display {
            fmt: ::core::clone::Clone::clone(&self.fmt),
            args: ::core::clone::Clone::clone(&self.args),
        }
    }
}Clone)]
6pub(crate) struct Display {
7    pub(crate) fmt: LitStr,
8    pub(crate) args: TokenStream,
9}
10
11pub(crate) struct VariantDisplay {
12    pub(crate) r#enum: Option<Display>,
13    pub(crate) variant: Display,
14}
15
16impl ToTokens for Display {
17    fn to_tokens(&self, tokens: &mut TokenStream) {
18        let fmt = &self.fmt;
19        let args = &self.args;
20        tokens.extend({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "write");
    ::quote::__private::push_bang(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "formatter");
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&fmt, &mut _s);
            ::quote::ToTokens::to_tokens(&args, &mut _s);
            _s
        });
    _s
}quote! {
21            write!(formatter, #fmt #args)
22        });
23    }
24}
25
26impl ToTokens for VariantDisplay {
27    fn to_tokens(&self, tokens: &mut TokenStream) {
28        if let Some(ref r#enum) = self.r#enum {
29            r#enum.to_tokens(tokens);
30            tokens.extend({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_question(&mut _s);
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_ident(&mut _s, "write");
    ::quote::__private::push_bang(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "formatter");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::parse(&mut _s, "\": \"");
            _s
        });
    ::quote::__private::push_question(&mut _s);
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! { ?; write!(formatter, ": ")?; });
31        }
32        self.variant.to_tokens(tokens);
33    }
34}
35
36pub(crate) struct AttrsHelper {
37    ignore_extra_doc_attributes: bool,
38    prefix_enum_doc_attributes: bool,
39}
40
41impl AttrsHelper {
42    pub(crate) fn new(attrs: &[Attribute]) -> Self {
43        let ignore_extra_doc_attributes = attrs
44            .iter()
45            .any(|attr| attr.path().is_ident("ignore_extra_doc_attributes"));
46        let prefix_enum_doc_attributes = attrs
47            .iter()
48            .any(|attr| attr.path().is_ident("prefix_enum_doc_attributes"));
49
50        Self {
51            ignore_extra_doc_attributes,
52            prefix_enum_doc_attributes,
53        }
54    }
55
56    pub(crate) fn display(&self, attrs: &[Attribute]) -> Result<Option<Display>> {
57        let displaydoc_attr = attrs.iter().find(|attr| attr.path().is_ident("displaydoc"));
58
59        if let Some(displaydoc_attr) = displaydoc_attr {
60            let lit = displaydoc_attr
61                .parse_args()
62                .expect("#[displaydoc(\"foo\")] must contain string arguments");
63            let mut display = Display {
64                fmt: lit,
65                args: TokenStream::new(),
66            };
67
68            display.expand_shorthand();
69            return Ok(Some(display));
70        }
71
72        let literals = attrs
73            .iter()
74            .filter(|attr| attr.path().is_ident("doc"))
75            .map(|attr| match &attr.meta {
76                Meta::NameValue(syn::MetaNameValue {
77                    value:
78                        syn::Expr::Lit(syn::ExprLit {
79                            lit: syn::Lit::Str(lit),
80                            ..
81                        }),
82                    ..
83                }) => lit,
84                _ => ::core::panicking::panic("not implemented")unimplemented!(),
85            });
86
87        let span = match literals.clone().next() {
88            Some(lit) => lit.span(),
89            None => return Ok(None),
90        };
91
92        let strs = literals.map(|lit| {
93            // Make an attempt at cleaning up multiline doc comments.
94            let doc_str = lit
95                .value()
96                .lines()
97                .map(|line| line.trim().trim_start_matches('*').trim())
98                .collect::<Vec<&str>>()
99                .join("\n")
100                .trim()
101                .to_string();
102            (!doc_str.is_empty()).then(|| doc_str)
103        });
104
105        let joined = if self.ignore_extra_doc_attributes {
106            strs.take_while(|x| x.is_some()).collect::<Option<Vec<_>>>()
107        } else {
108            strs.collect::<Option<Vec<_>>>()
109        }.unwrap_or_else(|| {
110            {
    ::core::panicking::panic_fmt(format_args!("Paragraph breaks in multi-line doc comments are disabled by default by displaydoc. Please consider using block doc comments (/** */) or adding the #[ignore_extra_doc_attributes] attribute to your type next to the derive"));
};panic!("Paragraph breaks in multi-line doc comments are disabled by default by displaydoc. Please consider using block doc comments (/** */) or adding the #[ignore_extra_doc_attributes] attribute to your type next to the derive");
111        }).join(" ");
112
113        let mut display = Display {
114            fmt: LitStr::new(&joined, span),
115            args: TokenStream::new(),
116        };
117
118        display.expand_shorthand();
119        Ok(Some(display))
120    }
121
122    pub(crate) fn display_with_input(
123        &self,
124        r#enum: &[Attribute],
125        variant: &[Attribute],
126    ) -> Result<Option<VariantDisplay>> {
127        let r#enum = if self.prefix_enum_doc_attributes {
128            let result = self
129                .display(r#enum)?
130                .expect("Missing doc comment on enum with #[prefix_enum_doc_attributes]. Please remove the attribute or add a doc comment to the enum itself.");
131
132            Some(result)
133        } else {
134            None
135        };
136
137        Ok(self
138            .display(variant)?
139            .map(|variant| VariantDisplay { r#enum, variant }))
140    }
141}