cargo_rdme/transform/
rust_remove_comments.rs1use crate::Doc;
2use crate::transform::DocTransform;
3use crate::transform::utils::rust_code_block_iterator;
4use crate::utils::ItemOrOther;
5use std::convert::Infallible;
6
7pub struct DocTransformRustRemoveComments;
8
9impl DocTransformRustRemoveComments {
10 #[must_use]
11 pub fn new() -> DocTransformRustRemoveComments {
12 DocTransformRustRemoveComments
13 }
14}
15
16fn is_line_commented(line: &str) -> bool {
17 line.trim_start().starts_with("# ") || line.trim() == "#"
18}
19
20fn process_code_block(new_doc_str: &mut String, code_block: &str) {
21 let mut first = true;
22
23 for (i, line) in code_block.split('\n').enumerate() {
24 if i == 0 && is_line_commented(line) {
28 while !new_doc_str.ends_with('\n') && !new_doc_str.is_empty() {
29 new_doc_str.pop();
30 }
31 }
32
33 if !is_line_commented(line) {
34 if !first {
35 new_doc_str.push('\n');
36 }
37
38 match line.trim_start().starts_with("##") {
41 true => new_doc_str.push_str(&line.replacen('#', "", 1)),
42 false => new_doc_str.push_str(line),
43 }
44
45 first = false;
46 }
47 }
48}
49
50impl DocTransform for DocTransformRustRemoveComments {
51 type E = Infallible;
52
53 fn transform(&self, doc: &Doc) -> Result<Doc, Infallible> {
54 let mut new_doc_str = String::new();
55
56 for item_or_other in rust_code_block_iterator(&doc.markdown).complete() {
57 match item_or_other {
58 ItemOrOther::Item(code_block) => {
59 process_code_block(&mut new_doc_str, code_block);
60 }
61 ItemOrOther::Other(other) => {
62 new_doc_str.push_str(other);
63 }
64 }
65 }
66
67 Ok(Doc::from_str(new_doc_str))
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use indoc::indoc;
75 use pretty_assertions::assert_eq;
76
77 #[test]
78 fn test_remove_comments_no_code_block() {
79 let doc_str = indoc! { r#"
80 # The crate
81
82 Look a this code:
83
84 That's all! Have a nice day!
85 "#
86 };
87
88 let expected_str = indoc! { r#"
89 # The crate
90
91 Look a this code:
92
93 That's all! Have a nice day!
94 "#
95 };
96
97 let doc = Doc::from_str(doc_str);
98 let expected = Doc::from_str(expected_str);
99
100 let transform = DocTransformRustRemoveComments::new();
101
102 assert_eq!(transform.transform(&doc).unwrap(), expected);
103 }
104
105 #[test]
106 fn test_remove_comments_fenced_code_block() {
107 let doc_str = indoc! { r#"
108 # The crate
109
110 Look a this code:
111
112 ```
113 println!("Hi");
114 println!("There");
115 ```
116
117 A second one:
118
119 ```
120 # A comment.
121 println!("Hi");
122 println!("There");
123 ```
124
125 And so one:
126
127 ```
128 println!("Hi");
129 # A comment.
130 println!("There");
131 ```
132
133 And so forth:
134
135 ```
136 println!("Hi");
137 println!("There");
138 # A comment.
139 ```
140
141 That's all! Have a nice day!
142 "#
143 };
144
145 let expected_str = indoc! { r#"
146 # The crate
147
148 Look a this code:
149
150 ```
151 println!("Hi");
152 println!("There");
153 ```
154
155 A second one:
156
157 ```
158 println!("Hi");
159 println!("There");
160 ```
161
162 And so one:
163
164 ```
165 println!("Hi");
166 println!("There");
167 ```
168
169 And so forth:
170
171 ```
172 println!("Hi");
173 println!("There");
174 ```
175
176 That's all! Have a nice day!
177 "#
178 };
179
180 let doc = Doc::from_str(doc_str);
181 let expected = Doc::from_str(expected_str);
182
183 let transform = DocTransformRustRemoveComments::new();
184
185 assert_eq!(transform.transform(&doc).unwrap(), expected);
186 }
187
188 #[test]
189 fn test_remove_comments_fenced_code_block_starting_with_whitespace() {
190 let doc_str = indoc! { r#"
191 # The crate
192
193 Look a this code:
194
195 ```
196 println!("Hi");
197 println!("There");
198 ```
199
200 A second one:
201
202 ```
203 # A comment.
204 println!("Hi");
205 println!("There");
206 ```
207
208 And so one:
209
210 ```
211 println!("Hi");
212 # A comment.
213 println!("There");
214 ```
215
216 And so forth:
217
218 ```
219 println!("Hi");
220 println!("There");
221 # A comment.
222 ```
223
224 That's all! Have a nice day!
225 "#
226 };
227
228 let expected_str = indoc! { r#"
229 # The crate
230
231 Look a this code:
232
233 ```
234 println!("Hi");
235 println!("There");
236 ```
237
238 A second one:
239
240 ```
241 println!("Hi");
242 println!("There");
243 ```
244
245 And so one:
246
247 ```
248 println!("Hi");
249 println!("There");
250 ```
251
252 And so forth:
253
254 ```
255 println!("Hi");
256 println!("There");
257 ```
258
259 That's all! Have a nice day!
260 "#
261 };
262
263 let doc = Doc::from_str(doc_str);
264 let expected = Doc::from_str(expected_str);
265
266 let transform = DocTransformRustRemoveComments::new();
267
268 assert_eq!(transform.transform(&doc).unwrap(), expected);
269 }
270
271 #[test]
272 fn test_remove_comments_indent_code_block() {
273 let doc_str = indoc! { r#"
274 # The crate
275
276 Look a this code:
277
278 println!("Hi");
279 println!("There");
280
281 A second one:
282
283 # A comment.
284 println!("Hi");
285 println!("There");
286
287 And so one:
288
289 println!("Hi");
290 # A comment.
291 println!("There");
292
293 And so forth:
294
295 println!("Hi");
296 println!("There");
297 # A comment.
298
299 That's all! Have a nice day!
300 "#
301 };
302
303 let expected_str = indoc! { r#"
304 # The crate
305
306 Look a this code:
307
308 println!("Hi");
309 println!("There");
310
311 A second one:
312
313 println!("Hi");
314 println!("There");
315
316 And so one:
317
318 println!("Hi");
319 println!("There");
320
321 And so forth:
322
323 println!("Hi");
324 println!("There");
325
326 That's all! Have a nice day!
327 "#
328 };
329
330 let doc = Doc::from_str(doc_str);
331 let expected = Doc::from_str(expected_str);
332
333 let transform = DocTransformRustRemoveComments::new();
334
335 assert_eq!(transform.transform(&doc).unwrap(), expected);
336 }
337
338 #[test]
339 fn test_remove_comments_indent_code_block_empty_lines() {
340 let doc_str = indoc! { r#"
341 # The crate
342
343 Look a this code:
344
345 println!("Hi");
346
347 println!("There");
348
349 That's all! Have a nice day!
350 "#
351 };
352
353 let expected_str = indoc! { r#"
354 # The crate
355
356 Look a this code:
357
358 println!("Hi");
359
360 println!("There");
361
362 That's all! Have a nice day!
363 "#
364 };
365
366 let doc = Doc::from_str(doc_str);
367 let expected = Doc::from_str(expected_str);
368
369 let transform = DocTransformRustRemoveComments::new();
370
371 assert_eq!(transform.transform(&doc).unwrap(), expected);
372 }
373
374 #[test]
375 fn test_remove_comments_indent_code_beginning_file_with_comment() {
376 let doc_str = indoc! { r#"
377 # Comment
378 println!("Hi");
379 # x
380 println!("There");
381
382 That's all! Have a nice day!
383 "#
384 };
385
386 assert!(doc_str.starts_with(" #"), "Ensure file starts correctly");
387
388 let expected_str = indoc! { r#"
389 println!("Hi");
390 println!("There");
391
392 That's all! Have a nice day!
393 "#
394 };
395
396 let doc = Doc::from_str(doc_str);
397 let expected = Doc::from_str(expected_str);
398
399 let transform = DocTransformRustRemoveComments::new();
400
401 assert_eq!(transform.transform(&doc).unwrap(), expected);
402 }
403
404 #[test]
405 fn test_remove_comments_indent_code_beginning_file_no_comment() {
406 let doc_str = indoc! { r#"
407 println!("Hi");
408 # x
409 println!("There");
410
411 That's all! Have a nice day!
412 "#
413 };
414
415 assert!(doc_str.starts_with(" println!"), "Ensure file starts correctly");
416
417 let expected_str = indoc! { r#"
418 println!("Hi");
419 println!("There");
420
421 That's all! Have a nice day!
422 "#
423 };
424
425 let doc = Doc::from_str(doc_str);
426 let expected = Doc::from_str(expected_str);
427
428 let transform = DocTransformRustRemoveComments::new();
429
430 assert_eq!(transform.transform(&doc).unwrap(), expected);
431 }
432
433 #[test]
434 fn test_remove_comments_identify_comment() {
435 let doc_str = indoc! { "
436 # The crate
437
438 Look a this code:
439
440 ```
441 # This is a comment
442 #This is not.
443 println!(\"There\");
444 #
445 # ↑ That line is a comment
446 #\t
447 # ↑ And so is that one.
448 ```
449 "
450 };
451
452 let expected_str = indoc! { r#"
453 # The crate
454
455 Look a this code:
456
457 ```
458 #This is not.
459 println!("There");
460 ```
461 "#
462 };
463
464 let doc = Doc::from_str(doc_str);
465 let expected = Doc::from_str(expected_str);
466
467 let transform = DocTransformRustRemoveComments::new();
468
469 assert_eq!(transform.transform(&doc).unwrap(), expected);
470 }
471
472 #[test]
473 fn test_remove_comments_double_hash_escape_comment() {
474 let doc_str = indoc! { r#"
475 ```
476 if true {
477 ## This is not a comment.
478 ##And neither is this.
479 println!("There");
480 }
481 ```
482 "#
483 };
484
485 let expected_str = indoc! { r#"
486 ```
487 if true {
488 # This is not a comment.
489 #And neither is this.
490 println!("There");
491 }
492 ```
493 "#
494 };
495
496 let doc = Doc::from_str(doc_str);
497 let expected = Doc::from_str(expected_str);
498
499 let transform = DocTransformRustRemoveComments::new();
500
501 assert_eq!(transform.transform(&doc).unwrap(), expected);
502 }
503
504 #[test]
505 fn test_remove_comments_for_known_code_block_tags() {
506 let tags = [
507 "should_panic",
508 "no_run",
509 "ignore",
510 "allow_fail",
511 "rust",
512 "test_harness",
513 "compile_fail",
514 "edition2018",
515 "ignore-foo",
516 ];
517
518 for tag in tags {
519 let doc_str = format!("```{tag}\n# This is a comment.\nprintln!(\"#There\");\n```\n");
520
521 let expected_str = format!("```{tag}\nprintln!(\"#There\");\n```\n");
522
523 let doc = Doc::from_str(doc_str);
524 let expected = Doc::from_str(expected_str);
525
526 let transform = DocTransformRustRemoveComments::new();
527
528 assert_eq!(transform.transform(&doc).unwrap(), expected);
529 }
530 }
531
532 #[test]
533 fn test_remove_comments_for_unknown_code_block_tags_no_change() {
534 let tags = ["text", "bash"];
535
536 for tag in tags {
537 let doc_str = format!("```{tag}\n# This is a comment.\nprintln!(\"#There\");\n```\n");
538 let doc = Doc::from_str(doc_str);
539
540 let transform = DocTransformRustRemoveComments::new();
541
542 assert_eq!(transform.transform(&doc).unwrap(), doc);
543 }
544 }
545
546 #[test]
547 fn test_remove_comments_nested_fenced_block() {
548 let doc_str = indoc! { r#"
549 ````
550 # Comment 1
551 let s = "
552 ```
553 ";
554 # Comment 2
555 println!("Hi");
556 let s = "
557 ```
558 ";
559 ````
560 "#
561 };
562
563 let expected_str = indoc! { r#"
564 ````
565 let s = "
566 ```
567 ";
568 println!("Hi");
569 let s = "
570 ```
571 ";
572 ````
573 "#
574 };
575
576 let doc = Doc::from_str(doc_str);
577 let expected = Doc::from_str(expected_str);
578
579 let transform = DocTransformRustRemoveComments::new();
580
581 assert_eq!(transform.transform(&doc).unwrap(), expected);
582 }
583}