| use proc_macro2::TokenStream; | |
| use quote::{quote, ToTokens}; | |
| use syn::{Attribute, LitStr, Meta, Result}; | |
| #[derive(Clone)] | |
| pub(crate) struct Display { | |
| pub(crate) fmt: LitStr, | |
| pub(crate) args: TokenStream, | |
| } | |
| pub(crate) struct VariantDisplay { | |
| pub(crate) r#enum: Option<Display>, | |
| pub(crate) variant: 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) | |
| }); | |
| } | |
| } | |
| impl ToTokens for VariantDisplay { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| if let Some(ref r#enum) = self.r#enum { | |
| r#enum.to_tokens(tokens); | |
| tokens.extend(quote! { ?; write!(formatter, ": ")?; }); | |
| } | |
| self.variant.to_tokens(tokens); | |
| } | |
| } | |
| pub(crate) struct AttrsHelper { | |
| ignore_extra_doc_attributes: bool, | |
| prefix_enum_doc_attributes: bool, | |
| } | |
| impl AttrsHelper { | |
| pub(crate) fn new(attrs: &[Attribute]) -> Self { | |
| let ignore_extra_doc_attributes = attrs | |
| .iter() | |
| .any(|attr| attr.path().is_ident("ignore_extra_doc_attributes")); | |
| let prefix_enum_doc_attributes = attrs | |
| .iter() | |
| .any(|attr| attr.path().is_ident("prefix_enum_doc_attributes")); | |
| Self { | |
| ignore_extra_doc_attributes, | |
| prefix_enum_doc_attributes, | |
| } | |
| } | |
| pub(crate) fn display(&self, attrs: &[Attribute]) -> Result<Option<Display>> { | |
| let displaydoc_attr = attrs.iter().find(|attr| attr.path().is_ident("displaydoc")); | |
| if let Some(displaydoc_attr) = displaydoc_attr { | |
| let lit = displaydoc_attr | |
| .parse_args() | |
| .expect("#[displaydoc(\"foo\")] must contain string arguments"); | |
| let mut display = Display { | |
| fmt: lit, | |
| args: TokenStream::new(), | |
| }; | |
| display.expand_shorthand(); | |
| return Ok(Some(display)); | |
| } | |
| let num_doc_attrs = attrs | |
| .iter() | |
| .filter(|attr| attr.path().is_ident("doc")) | |
| .count(); | |
| if !self.ignore_extra_doc_attributes && num_doc_attrs > 1 { | |
| panic!("Multi-line 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."); | |
| } | |
| for attr in attrs { | |
| if attr.path().is_ident("doc") { | |
| let lit = match &attr.meta { | |
| Meta::NameValue(syn::MetaNameValue { | |
| value: | |
| syn::Expr::Lit(syn::ExprLit { | |
| lit: syn::Lit::Str(lit), | |
| .. | |
| }), | |
| .. | |
| }) => lit, | |
| _ => unimplemented!(), | |
| }; | |
| // Make an attempt at cleaning up multiline doc comments. | |
| let doc_str = lit | |
| .value() | |
| .lines() | |
| .map(|line| line.trim().trim_start_matches('*').trim()) | |
| .collect::<Vec<&str>>() | |
| .join("\n"); | |
| let lit = LitStr::new(doc_str.trim(), lit.span()); | |
| let mut display = Display { | |
| fmt: lit, | |
| args: TokenStream::new(), | |
| }; | |
| display.expand_shorthand(); | |
| return Ok(Some(display)); | |
| } | |
| } | |
| Ok(None) | |
| } | |
| pub(crate) fn display_with_input( | |
| &self, | |
| r#enum: &[Attribute], | |
| variant: &[Attribute], | |
| ) -> Result<Option<VariantDisplay>> { | |
| let r#enum = if self.prefix_enum_doc_attributes { | |
| let result = self | |
| .display(r#enum)? | |
| .expect("Missing doc comment on enum with #[prefix_enum_doc_attributes]. Please remove the attribute or add a doc comment to the enum itself."); | |
| Some(result) | |
| } else { | |
| None | |
| }; | |
| Ok(self | |
| .display(variant)? | |
| .map(|variant| VariantDisplay { r#enum, variant })) | |
| } | |
| } |