mdbook_to_github_wiki/
lib.rs1use std::io::{BufReader, Read, Write};
2
3pub struct Builder {
4 name: String,
5 source: String,
6}
7
8impl Builder {
9 pub fn new() -> Builder {
10 Builder {
11 name: "wiki".to_string(),
12 source: "book".to_string(),
13 }
14 }
15
16 pub fn set_name(mut self, name: &str) -> Self {
17 self.name = name.to_string();
18 self
19 }
20 pub fn set_source(mut self, source: &str) -> Self {
21 self.source = source.to_string();
22 self
23 }
24
25 pub fn run(self) -> Result<(), std::io::Error> {
26 std::fs::create_dir_all(format!("{}", self.name))?;
27 let mut sidebar_md = std::fs::File::create(format!("{}/_Sidebar.md", self.name))?;
28 let mut lines: Vec<String> = vec\n".to_string()];
29
30 let summary_md = std::fs::File::open(format!("{}/src/SUMMARY.md", self.source))?;
31 let mut reader = BufReader::new(summary_md);
32 let mut summary_contents = "".to_string();
33 let _ = reader.read_to_string(&mut summary_contents);
34 let mut summary_lines: Vec<String> = summary_contents
35 .split("\n")
36 .filter(|n| n.contains("(") && n.contains("["))
37 .map(|n| n.to_string())
38 .collect();
39
40 #[derive(Debug)]
41 enum StringOrVec {
42 String(String),
43 Vec(Vec<StringOrVec>),
44 }
45
46 fn create_branch(lines: &mut Vec<String>, current_level: i32) -> Vec<StringOrVec> {
47 let mut branch: Vec<StringOrVec> = vec![];
48 while lines.len() > 0 {
49 let value = lines.remove(0);
50
51 let mut x: Vec<String> = value.split("").map(|n| n.to_string()).collect();
52 let mut spaces: Vec<String> = vec![];
53 if x[0] == "" {
54 x.remove(0);
55 }
56 while x[0] == " " {
57 spaces.push(x.remove(0));
58 }
59
60 let new_level = (spaces.len() / 2) as i32;
61
62 if current_level == new_level {
63 branch.push(StringOrVec::String(value));
64 } else if current_level < new_level {
65 lines.insert(0, value);
66 branch.push(StringOrVec::Vec(create_branch(lines, new_level)));
67 } else if current_level > new_level {
68 lines.insert(0, value);
69 return branch;
70 }
71 }
72 branch
73 }
74 let tree = create_branch(&mut summary_lines, 0);
75
76 fn add_lines(lines: &mut Vec<String>, tree: &Vec<StringOrVec>, source: String) {
77 for entry in tree {
78 match entry {
79 StringOrVec::String(s) => {
80 lines.push(format!(
81 "{}",
82 s.replace("./", "").replace("/", "-").replace(".md", "")
83 ));
84 }
85 StringOrVec::Vec(v) => {
86 add_lines(lines, v, source.to_string());
87 }
88 }
89 }
90 }
91 add_lines(&mut lines, &tree, self.source.clone());
92
93 lines.push("".to_string());
94 let contents = lines.join("\n");
95
96 sidebar_md.write_all(contents.as_bytes())?;
97
98 let _ = std::fs::copy("README.md", format!("{}/home.md", self.name))?;
99
100 let mut progression: Vec<String> = vec![];
101 for n in lines.iter().filter(|n| n.contains("- ")) {
102 let filename: String = n.split("(").collect::<Vec<&str>>()[1].replace(")", ".md");
103 progression.push(filename);
104 }
105
106 for entry in walkdir::WalkDir::new(format!("{}/src", self.source))
107 .min_depth(1)
108 .into_iter()
109 .filter(|n| n.is_ok())
110 .map(|n| n.unwrap())
111 {
112 if entry.file_name() == "SUMMARY.md" || entry.path().is_dir() {
113 continue;
114 }
115 let mut target = format!("{}", entry.path().to_str().unwrap());
116 target = target
117 .strip_prefix(format!("{}/src{}", self.source, std::path::MAIN_SEPARATOR).as_str())
118 .unwrap()
119 .to_string();
120 target = target.replace("\\", "-");
121 target = target.replace("/", "-");
122 target = format!("{}/{}", self.name, target);
123
124 let mut data = std::fs::read(entry.path())?;
125
126 let i = progression
127 .iter()
128 .position(|n| n.eq(&target.replace(format!("{}/", &self.name).as_str(), "")));
129 if i.is_some() {
130 let index = i.unwrap();
131 let p;
132 if index == 0 {
133 p = None;
134 } else {
135 p = progression.get(index - 1);
136 }
137 let n = progression.get(index + 1);
138 let previous;
139 let next;
140 if n.is_some() && p.is_none() {
142 next = n.unwrap().replace(".md", "");
143 data.splice(
144 0..0,
145 format!(
146 r#"<table>
147<tr>
148<td><a href="home">◀</a></td>
149<td width="9999" align="center"></td>
150<td><a href="{next}">▶</a></td>
151</tr>
152</table>
153
154"#
155 )
156 .bytes(),
157 );
158 }
159 else if n.is_none() && p.is_some() {
161 previous = p.unwrap().replace(".md", "");
162 data.splice(
163 0..0,
164 format!(
165 r#"<table>
166<tr>
167<td><a href="{previous}">◀</a></td>
168<td width="9999" align="center"></td>
169</tr>
170</table>
171
172"#
173 )
174 .bytes(),
175 );
176 }
177 else if n.is_some() && p.is_some() {
179 previous = p.unwrap().replace(".md", "");
180 next = n.unwrap().replace(".md", "");
181 data.splice(
182 0..0,
183 format!(
184 r#"<table>
185<tr>
186<td><a href="{previous}">◀</a></td>
187<td width="9999" align="center"></td>
188<td><a href="{next}">▶</a></td>
189</tr>
190</table>
191
192"#
193 )
194 .bytes(),
195 );
196 }
197 }
198
199 let _ = std::fs::write(&target, data);
200 }
201 Ok(())
202 }
203}