1#![cfg_attr(feature = "nightly", deny(missing_docs))]
2#![cfg_attr(feature = "nightly", feature(external_doc))]
3#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
4#![cfg_attr(test, deny(warnings))]
5
6extern crate pulldown_cmark;
7extern crate pulldown_cmark_to_cmark;
8#[macro_use]
9extern crate failure;
10
11use pulldown_cmark::{Event, Parser, Tag};
12use pulldown_cmark_to_cmark::fmt::cmark;
13use std::{fs, path};
14
15pub struct Markdown<'p> {
17 parser: Parser<'p>,
18}
19
20impl<'p> Markdown<'p> {
21 #[inline]
23 pub fn new(raw_md: &'p str) -> Self {
24 Self {
25 parser: Parser::new(raw_md),
26 }
27 }
28
29 pub fn replace_body(
31 self,
32 header: &str,
33 section: Vec<Event>,
34 ) -> Result<String, failure::Error> {
35 let mut inspect_header = false;
37 let mut header_found = false;
38 let mut should_replace_section = false;
39 let mut sections_replaced = false;
40
41 let events: Vec<Event> = {
42 self
44 .parser
45 .into_iter()
46 .flat_map(move |event| {
47 if let Event::Start(tag) = &event {
49 if let Tag::Header(_text) = tag {
50 inspect_header = true;
51 }
52 }
53
54 if inspect_header {
56 inspect_header = false;
57 if let Event::Text(text) = &event {
58 if text == header {
59 header_found = true;
60 }
61 }
62 }
63
64 if should_replace_section {
66 let mut should_continue = true;
67 if let Event::Start(tag) = &event {
68 if let Tag::Header(_) = tag {
69 should_replace_section = false;
70 should_continue = false;
71 }
72 }
73
74 if should_continue {
75 return if !sections_replaced {
76 sections_replaced = true;
77 section.clone()
79 } else {
80 vec![]
81 };
82 }
83 }
84
85 vec![event]
87 })
88 .collect()
89 };
90
91 if sections_replaced {
92 let mut buf = String::from("");
93 cmark(events.iter(), &mut buf, None)?;
94 Ok(buf)
95 } else if header_found {
96 bail!("No header body found to replace.");
97 } else {
98 bail!("Could not find header");
99 }
100 }
101}
102
103pub fn replace_body(
105 path: path::PathBuf,
106 header: &str,
107 body: String,
108) -> Result<(), failure::Error> {
109 let target = fs::read_to_string(&path)?;
110 let body: Vec<Event> = Parser::new(&body).into_iter().collect();
111 let parser = Markdown::new(&target);
112 let res = parser.replace_body(header, body)?;
113 fs::write(&path, res)?;
114 Ok(())
115}