mdbook_to_example/
lib.rs

1use std::io::{BufReader, Read, Write};
2
3pub struct Builder {
4    name: String,
5    source: String,
6    logo_url: String,
7    as_comment: bool,
8}
9
10impl Builder {
11    pub fn new() -> Builder {
12        Builder {
13            name: "book".to_string(),
14            source: "book".to_string(),
15            logo_url: "".to_string(),
16            as_comment: false,
17        }
18    }
19
20    pub fn set_name(mut self, name: &str) -> Self {
21        self.name = name.to_string();
22        self
23    }
24    pub fn set_source(mut self, source: &str) -> Self {
25        self.source = source.to_string();
26        self
27    }
28    pub fn set_logo_url(mut self, logo_url: &str) -> Self {
29        self.logo_url = logo_url.to_string();
30        self
31    }
32    pub fn include_as_comment(mut self, as_comment: bool) -> Self {
33        self.as_comment = as_comment;
34        self
35    }
36
37    pub fn run(self) -> Result<(), std::io::Error> {
38        std::fs::create_dir_all(format!("examples/{}", self.name))?;
39        let mut main_rs = std::fs::File::create(format!("examples/{}/main.rs", self.name))?;
40        let readme_md = std::fs::File::open("README.md")?;
41        let mut reader = BufReader::new(readme_md);
42        let mut contents = "".to_string();
43        let _ = reader.read_to_string(&mut contents);
44
45        let mut lines: Vec<String> = vec![];
46
47        if self.logo_url != "" {
48            lines.push(format!("#![doc(html_logo_url = \"{}\")]", self.logo_url));
49        }
50
51        let mut readme_lines: Vec<String> = contents.split("\n").map(|n| format!("//! {}", n)).collect();
52        lines.append(&mut readme_lines);
53        lines.push("\nfn main () {}\n".to_string());
54
55        let summary_md = std::fs::File::open(format!("{}/src/SUMMARY.md", self.source))?;
56        let mut reader = BufReader::new(summary_md);
57        let mut summary_contents = "".to_string();
58        let _ = reader.read_to_string(&mut summary_contents);
59        let mut summary_lines: Vec<String> = summary_contents.split("\n")
60            .filter(|n| n.contains("(") && n.contains("["))
61            .map(|n| n.to_string())
62            .collect();
63
64        #[derive(Debug)]
65        enum StringOrVec {
66            String(String),
67            Vec(Vec<StringOrVec>),
68        }
69
70        fn create_branch(lines: &mut Vec<String>, current_level: i32) -> Vec<StringOrVec> {
71            let mut branch: Vec<StringOrVec> = vec![];
72            while lines.len() > 0 {
73                let value = lines.remove(0);
74
75                let mut x: Vec<String> = value.split("").map(|n| n.to_string()).collect();
76                let mut spaces: Vec<String> = vec![];
77                if x[0] == "" {
78                    x.remove(0);
79                }
80                while x[0] == " " {
81                    spaces.push(x.remove(0));
82                }
83
84                let new_level = (spaces.len() / 2) as i32;
85
86                if current_level == new_level {
87                    branch.push(StringOrVec::String(value));
88                } else if current_level < new_level {
89                    lines.insert(0, value);
90                    branch.push(StringOrVec::Vec(create_branch(lines, new_level)));
91                } else if current_level > new_level {
92                    lines.insert(0, value);
93                    return branch;
94                }
95            }
96            branch
97        }
98        let tree = create_branch(&mut summary_lines, 0);
99
100
101        fn add_lines(lines: &mut Vec<String>, tree: Vec<StringOrVec>, source: String, as_comment: bool) -> Result<(), std::io::Error> {
102            let mut level = 0;
103            for entry in tree {
104                match entry {
105                    StringOrVec::String(s) => {
106                        level += 1;
107                        let m_header: Vec<String> = s.trim().split("[").map(|n| n.to_string()).collect();
108                        let n_header: Vec<String> = m_header[1].split("]").map(|n| n.to_string()).collect();
109                        let header = n_header[0].to_lowercase().replace(" ", "_");
110
111                        let m_link: Vec<String> = s.trim().split("(").map(|n| n.to_string()).collect();
112                        let link: Vec<String> = m_link[1].split(")").map(|n| n.to_string()).collect();
113
114
115                        if as_comment {
116                            println!("{}/src/{}", source, link[0]);
117                            let source_file = std::fs::File::open(format!("{}/src/{}", source, link[0]))?;
118                            let mut reader = BufReader::new(source_file);
119                            let mut contents = "".to_string();
120                            let _ = reader.read_to_string(&mut contents);
121                            let mut source_lines: Vec<String> = contents.split("\n").map(|n| format!("/// {}", n)).collect();
122                            lines.append(&mut source_lines);
123                        } else {
124                            lines.push(format!("#[doc = include_str!(\"../../{}/src/{}\")]", source.to_string(), link[0]));
125                        }
126                        lines.push(format!("pub mod s{}_{} {{}}", level, header));
127                    }
128                    StringOrVec::Vec(v) => {
129                        let old = lines.remove(lines.len() - 1);
130                        lines.push(format!("{}", old.strip_suffix("}").unwrap()));
131                        let _ = add_lines(lines, v, source.to_string(), as_comment);
132                        lines.push(format!("}}\n"));
133                    }
134                }
135            }
136            Ok(())
137        }
138        let _ = add_lines(&mut lines, tree, self.source, self.as_comment);
139
140
141        lines.push("".to_string());
142        contents = lines.join("\n");
143
144
145        main_rs.write_all(contents.as_bytes())?;
146        Ok(())
147    }
148}