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 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}