opencv_binding_generator/
comment.rs1use crate::string_ext::Indent;
2use crate::StrExt;
3
4pub fn strip_doxygen_comment_markers(comment: &str) -> String {
5 const MULTILINE_PREFIX: &str = "/*";
7 const MULTILINE_CONT: &str = "*";
8 const MULTILINE_SUFFIX: &str = "*/";
9 const SINGLELINE: &str = "//";
10 const DETAIL: &str = "!";
11 const SINGLELINE_DETAIL: &str = "/";
12 const SINGLELINE_SIDE: &str = "<";
13
14 fn trim_last_empty_lines(lines: &mut Vec<&str>) {
15 while lines.last().is_some_and(|line| line.is_empty()) {
16 lines.pop();
17 }
18 }
19
20 let mut comment_type = CommentType::SingleLineDelimited;
21 let mut lines = Vec::with_capacity(128);
22 for (i, mut line) in comment.lines_with_nl().enumerate() {
28 let mut line_trimmed = line.trim_start();
29 if i == 0 {
30 if let Some(new_line) = line_trimmed.strip_prefix(MULTILINE_PREFIX) {
31 line = new_line;
32 if let Some(new_line) = line.strip_prefix(MULTILINE_CONT) {
33 line = new_line;
34 comment_type = CommentType::MultilineAsteriskPrefixed;
35 } else {
36 comment_type = CommentType::MultilineWithoutAsteriskPrefix;
37 }
38 line_trimmed = line.trim_start();
39 }
40 } else if let Some(line_clean) = line_trimmed.strip_prefix(MULTILINE_PREFIX) {
41 line = line_clean
42 .trim_start_matches(DETAIL)
43 .trim_start_matches(MULTILINE_CONT)
44 .trim_start();
45 }
46 if comment_type == CommentType::SingleLineDelimited {
47 if let Some(new_line) = line_trimmed.strip_prefix(SINGLELINE) {
48 line = new_line
49 .strip_prefix(SINGLELINE_DETAIL)
50 .or_else(|| new_line.strip_prefix(DETAIL))
51 .unwrap_or(new_line);
52 line = line.strip_prefix(SINGLELINE_SIDE).unwrap_or(line);
53 }
54 } else if i == 1 && comment_type == CommentType::MultilineAsteriskPrefixed && !line_trimmed.starts_with(MULTILINE_CONT) {
55 comment_type = CommentType::MultilineWithoutAsteriskPrefix;
56 }
57 lines.push(line);
58 }
59 trim_last_empty_lines(&mut lines);
60 if let Some(last_line) = lines.last_mut() {
62 if comment_type != CommentType::SingleLineDelimited {
63 *last_line = last_line.trim_end();
64 if let Some(new_line) = last_line.strip_suffix(MULTILINE_SUFFIX) {
65 *last_line = new_line.trim_end();
66 }
67 }
68 }
69 trim_last_empty_lines(&mut lines);
70 let mut first_line_indent = None;
74 let mut common_indent: Option<Indent> = None;
75 for line in &mut lines {
76 if comment_type == CommentType::MultilineAsteriskPrefixed {
77 let line_trimmed = line.trim_start();
78 if let Some(line_trimmed) = line_trimmed.strip_prefix(MULTILINE_CONT) {
79 *line = line_trimmed;
80 } else {
81 let trim_start = line.trim_start_idx().min(2);
82 *line = &line[trim_start..];
83 }
84 }
85 if first_line_indent.is_none() {
86 first_line_indent = Some(line.detect_indent());
87 } else {
88 let detected_indent = line.detect_indent();
89 if !line[detected_indent.len..].trim_start().is_empty() {
90 if let Some(common_indent) = common_indent.as_mut() {
91 *common_indent = (*common_indent).min(detected_indent);
92 } else {
93 common_indent = Some(line.detect_indent());
94 }
95 }
96 }
97 }
98 let mut out = String::with_capacity(comment.len());
99 for (i, mut line) in lines.into_iter().enumerate() {
100 if i == 0 {
101 line = &line[first_line_indent.unwrap_or_default().len..];
102 if line.trim().is_empty() {
103 continue;
104 }
105 } else {
106 let indent_len = common_indent.unwrap_or_default().len;
107 if line.len() > indent_len {
108 line = &line[indent_len..];
109 } else {
110 line = "\n";
111 }
112 }
113 let line_clean_end = line.trim_end();
114 if let Some(suffix) = line_clean_end.strip_suffix(MULTILINE_SUFFIX) {
115 out += suffix.trim_end();
116 out.push('\n');
117 } else {
118 out += line;
119 }
120 }
121
122 let out_trim_end = out.trim_end_idx();
123 out.drain(out_trim_end..);
124 out
125}
126
127#[derive(PartialEq)]
128enum CommentType {
129 SingleLineDelimited,
131 MultilineAsteriskPrefixed,
133 MultilineWithoutAsteriskPrefix,
135}
136
137#[cfg(test)]
138mod test {
139 use super::strip_doxygen_comment_markers;
140
141 #[test]
142 fn test_strip_comment_markers() {
143 assert_eq!("test", &strip_doxygen_comment_markers("/** test */"));
144 assert_eq!("test", &strip_doxygen_comment_markers("// test"));
145 assert_eq!("test", &strip_doxygen_comment_markers("/*test */"));
146
147 {
149 let comment = "\
150/**
151 * line1
152 * line2
153 */
154";
155 let expected = "\
156line1
157line2";
158 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
159 }
160
161 {
163 let comment = "\
164/*
165line1
166 line2
167*/
168";
169 let expected = "\
170line1
171 line2";
172 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
173 }
174
175 {
177 let comment = "\
178/** line1
179 * line2
180 * line3
181";
182 let expected = "\
183line1
184line2
185line3";
186 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
187 }
188
189 {
191 let comment = "\
192/* line1
193 * line2
194 line3
195 * line4
196*/
197";
198 let expected = "\
199line1
200* line2
201 line3
202* line4";
203 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
204 }
205
206 {
208 let comment = "\
209/**
210 * line1
211 * line2
212 line3
213 */
214";
215 let expected = "\
216line1
217line2
218 line3";
219 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
220 }
221
222 {
224 let comment = "\
225/** line1
226 line2
227 line3*/
228";
229 let expected = "\
230line1
231line2
232line3";
233 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
234 }
235
236 {
238 let comment = "\
239/** line1
240 line2*/
241/** line3 */
242";
243 let expected = "\
244line1
245 line2
246line3";
247 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
248 }
249
250 {
252 let comment = "\
253/** line1
254
255line2
256
257*/
258";
259 let expected = "\
260line1
261
262line2";
263 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
264 }
265
266 {
268 let comment = "\
269/**
270 line1
271
272 line2
273*/
274";
275 let expected = "\
276line1
277
278line2";
279 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
280 }
281
282 {
284 let comment = "\
285/** line1
286
287 line2
288 */
289";
290 let expected = "\
291line1
292
293line2";
294 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
295 }
296
297 {
299 let comment = "\
300//!< line1
301 //!< line2
302";
303 let expected = "\
304line1
305line2";
306 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
307 }
308
309 {
311 let comment = "// @overload
312// @brief It's the same function as #calibrateCameraAruco but without calibration error estimation.
313//";
314
315 let expected = "@overload
316@brief It's the same function as #calibrateCameraAruco but without calibration error estimation.";
317 assert_eq!(expected, &strip_doxygen_comment_markers(comment));
318 }
319 }
320}