schemars_derive/attr/
doc.rs

1use syn::Attribute;
2
3pub fn get_title_and_desc_from_doc(attrs: &[Attribute]) -> (Option<String>, Option<String>) {
4    let doc = match get_doc(attrs) {
5        None => return (None, None),
6        Some(doc) => doc,
7    };
8
9    if doc.starts_with('#') {
10        let mut split = doc.splitn(2, '\n');
11        let title = split
12            .next()
13            .unwrap()
14            .trim_start_matches('#')
15            .trim()
16            .to_owned();
17        let maybe_desc = split.next().and_then(merge_description_lines);
18        (none_if_empty(title), maybe_desc)
19    } else {
20        (None, merge_description_lines(&doc))
21    }
22}
23
24fn merge_description_lines(doc: &str) -> Option<String> {
25    let desc = doc
26        .trim()
27        .split("\n\n")
28        .filter_map(|line| none_if_empty(line.trim().replace('\n', " ")))
29        .collect::<Vec<_>>()
30        .join("\n\n");
31    none_if_empty(desc)
32}
33
34fn get_doc(attrs: &[Attribute]) -> Option<String> {
35    let attrs = attrs
36        .iter()
37        .filter_map(|attr| {
38            if !attr.path().is_ident("doc") {
39                return None;
40            }
41
42            let meta = attr.meta.require_name_value().ok()?;
43            if let syn::Expr::Lit(syn::ExprLit {
44                lit: syn::Lit::Str(lit_str),
45                ..
46            }) = &meta.value
47            {
48                return Some(lit_str.value());
49            }
50
51            None
52        })
53        .collect::<Vec<_>>();
54
55    let mut lines = attrs
56        .iter()
57        .flat_map(|a| a.split('\n'))
58        .map(str::trim)
59        .skip_while(|s| s.is_empty())
60        .collect::<Vec<_>>();
61
62    if let Some(&"") = lines.last() {
63        lines.pop();
64    }
65
66    // Added for backward-compatibility, but perhaps we shouldn't do this
67    // https://github.com/rust-lang/rust/issues/32088
68    if lines.iter().all(|l| l.starts_with('*')) {
69        for line in lines.iter_mut() {
70            *line = line[1..].trim()
71        }
72        while let Some(&"") = lines.first() {
73            lines.remove(0);
74        }
75    };
76
77    none_if_empty(lines.join("\n"))
78}
79
80fn none_if_empty(s: String) -> Option<String> {
81    if s.is_empty() {
82        None
83    } else {
84        Some(s)
85    }
86}