mithril_doc_derive/
doc.rs1#![allow(dead_code)]
11
12use std::iter;
13
14pub fn extract_doc_comment(attrs: &[syn::Attribute]) -> Vec<String> {
15 let mut lines: Vec<_> = attrs
20 .iter()
21 .filter(|attr| attr.path().is_ident("doc"))
22 .filter_map(|attr| {
23 match &attr.meta {
26 syn::Meta::NameValue(syn::MetaNameValue {
27 value:
28 syn::Expr::Lit(syn::ExprLit {
29 lit: syn::Lit::Str(s),
30 ..
31 }),
32 ..
33 }) => Some(s.value()),
34 _ => None,
35 }
36 })
37 .skip_while(|s| is_blank(s))
38 .flat_map(|s| {
39 s.split('\n')
40 .map(|s| {
41 let s = s.strip_prefix(' ').unwrap_or(s);
43 s.to_owned()
44 })
45 .collect::<Vec<_>>()
46 })
47 .collect();
48
49 while let Some(true) = lines.last().map(|s| is_blank(s)) {
50 lines.pop();
51 }
52
53 lines
54}
55
56pub fn format_doc_comment(
57 lines: &[String],
58 preprocess: bool,
59 force_long: bool,
60) -> (Option<String>, Option<String>) {
61 if let Some(first_blank) = lines.iter().position(|s| is_blank(s)) {
62 let (short, long) = if preprocess {
63 let paragraphs = split_paragraphs(lines);
64 let short = paragraphs[0].clone();
65 let long = paragraphs.join("\n\n");
66 (remove_period(short), long)
67 } else {
68 let short = lines[..first_blank].join("\n");
69 let long = lines.join("\n");
70 (short, long)
71 };
72
73 (Some(short), Some(long))
74 } else {
75 let (short, long) = if preprocess {
76 let short = merge_lines(lines);
77 let long = force_long.then(|| short.clone());
78 let short = remove_period(short);
79 (short, long)
80 } else {
81 let short = lines.join("\n");
82 let long = force_long.then(|| short.clone());
83 (short, long)
84 };
85
86 (Some(short), long)
87 }
88}
89
90fn split_paragraphs(lines: &[String]) -> Vec<String> {
91 let mut last_line = 0;
92 iter::from_fn(|| {
93 let slice = &lines[last_line..];
94 let start = slice.iter().position(|s| !is_blank(s)).unwrap_or(0);
95
96 let slice = &slice[start..];
97 let len = slice.iter().position(|s| is_blank(s)).unwrap_or(slice.len());
98
99 last_line += start + len;
100
101 if len != 0 {
102 Some(merge_lines(&slice[..len]))
103 } else {
104 None
105 }
106 })
107 .collect()
108}
109
110fn remove_period(mut s: String) -> String {
111 if s.ends_with('.') && !s.ends_with("..") {
112 s.pop();
113 }
114 s
115}
116
117fn is_blank(s: &str) -> bool {
118 s.trim().is_empty()
119}
120
121fn merge_lines(lines: impl IntoIterator<Item = impl AsRef<str>>) -> String {
122 lines
123 .into_iter()
124 .map(|s| s.as_ref().trim().to_owned())
125 .collect::<Vec<_>>()
126 .join(" ")
127}