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