utoipa_gen/
doc_comment.rs

1use std::ops::Deref;
2
3use proc_macro2::Ident;
4use syn::{Attribute, Expr, Lit, Meta};
5
6const DOC_ATTRIBUTE_TYPE: &str = "doc";
7
8/// CommentAttributes holds Vec of parsed doc comments
9#[cfg_attr(feature = "debug", derive(Debug))]
10pub(crate) struct CommentAttributes(pub(crate) Vec<String>);
11
12impl CommentAttributes {
13    /// Creates new [`CommentAttributes`] instance from [`Attribute`] slice filtering out all
14    /// other attributes which are not `doc` comments
15    pub(crate) fn from_attributes(attributes: &[Attribute]) -> Self {
16        Self(Self::as_string_vec(
17            attributes.iter().filter(Self::is_doc_attribute),
18        ))
19    }
20
21    fn is_doc_attribute(attribute: &&Attribute) -> bool {
22        match Self::get_attribute_ident(attribute) {
23            Some(attribute) => attribute == DOC_ATTRIBUTE_TYPE,
24            None => false,
25        }
26    }
27
28    fn get_attribute_ident(attribute: &Attribute) -> Option<&Ident> {
29        attribute.path().get_ident()
30    }
31
32    fn as_string_vec<'a, I: Iterator<Item = &'a Attribute>>(attributes: I) -> Vec<String> {
33        attributes
34            .into_iter()
35            .filter_map(Self::parse_doc_comment)
36            .collect()
37    }
38
39    fn parse_doc_comment(attribute: &Attribute) -> Option<String> {
40        match &attribute.meta {
41            Meta::NameValue(name_value) => {
42                if let Expr::Lit(ref doc_comment) = name_value.value {
43                    if let Lit::Str(ref comment) = doc_comment.lit {
44                        Some(comment.value().trim().to_string())
45                    } else {
46                        None
47                    }
48                } else {
49                    None
50                }
51            }
52            // ignore `#[doc(hidden)]` and similar tags.
53            _ => None,
54        }
55    }
56
57    /// Returns found `doc comments` as formatted `String` joining them all with `\n` _(new line)_.
58    pub(crate) fn as_formatted_string(&self) -> String {
59        self.join("\n")
60    }
61}
62
63impl Deref for CommentAttributes {
64    type Target = Vec<String>;
65
66    fn deref(&self) -> &Self::Target {
67        &self.0
68    }
69}