2022-05-03 23:05:55 -07:00
|
|
|
|
use {
|
2022-06-12 18:02:09 -07:00
|
|
|
|
pulldown_cmark::{CowStr, Event, HeadingLevel, Options, Parser, Tag},
|
2022-05-03 23:05:55 -07:00
|
|
|
|
pulldown_cmark_to_cmark::cmark,
|
2022-09-11 02:25:38 -07:00
|
|
|
|
std::{collections::BTreeMap, error::Error, fmt::Write, fs, ops::Deref},
|
2022-05-03 23:05:55 -07:00
|
|
|
|
};
|
|
|
|
|
|
2022-06-12 18:02:09 -07:00
|
|
|
|
type Result<T = ()> = std::result::Result<T, Box<dyn Error>>;
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
2022-06-08 18:04:06 -07:00
|
|
|
|
enum Language {
|
|
|
|
|
English,
|
|
|
|
|
Chinese,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Language {
|
|
|
|
|
fn code(&self) -> &'static str {
|
|
|
|
|
match self {
|
|
|
|
|
Self::English => "en",
|
|
|
|
|
Self::Chinese => "zh",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn suffix(&self) -> &'static str {
|
|
|
|
|
match self {
|
|
|
|
|
Self::English => "",
|
|
|
|
|
Self::Chinese => ".中文",
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-08 21:48:44 -07:00
|
|
|
|
|
|
|
|
|
fn introduction(&self) -> &'static str {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Chinese => "说明",
|
|
|
|
|
Self::English => "Introduction",
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-08 18:04:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-12 18:02:09 -07:00
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct Chapter<'a> {
|
|
|
|
|
level: HeadingLevel,
|
|
|
|
|
events: Vec<Event<'a>>,
|
|
|
|
|
index: usize,
|
|
|
|
|
language: Language,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Chapter<'a> {
|
|
|
|
|
fn title(&self) -> String {
|
|
|
|
|
if self.index == 0 {
|
|
|
|
|
return self.language.introduction().into();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self
|
|
|
|
|
.events
|
|
|
|
|
.iter()
|
|
|
|
|
.skip_while(|event| !matches!(event, Event::Start(Tag::Heading(..))))
|
|
|
|
|
.skip(1)
|
|
|
|
|
.take_while(|event| !matches!(event, Event::End(Tag::Heading(..))))
|
|
|
|
|
.filter_map(|event| match event {
|
|
|
|
|
Event::Code(content) | Event::Text(content) => Some(content.deref()),
|
|
|
|
|
_ => None,
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn number(&self) -> usize {
|
|
|
|
|
self.index + 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn markdown(&self) -> Result<String> {
|
|
|
|
|
let mut markdown = String::new();
|
|
|
|
|
cmark(self.events.iter(), &mut markdown)?;
|
|
|
|
|
if self.index == 0 {
|
|
|
|
|
markdown = markdown.split_inclusive('\n').skip(1).collect::<String>();
|
|
|
|
|
}
|
|
|
|
|
Ok(markdown)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn slug(s: &str) -> String {
|
|
|
|
|
let mut slug = String::new();
|
|
|
|
|
for c in s.chars() {
|
|
|
|
|
match c {
|
|
|
|
|
'A'..='Z' => slug.extend(c.to_lowercase()),
|
|
|
|
|
' ' => slug.push('-'),
|
|
|
|
|
'?' | '.' | '?' => {}
|
|
|
|
|
_ => slug.push(c),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
slug
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result {
|
2022-06-08 18:04:06 -07:00
|
|
|
|
for language in [Language::English, Language::Chinese] {
|
|
|
|
|
let src = format!("book/{}/src", language.code());
|
|
|
|
|
fs::remove_dir_all(&src).ok();
|
|
|
|
|
fs::create_dir(&src)?;
|
2022-05-03 23:05:55 -07:00
|
|
|
|
|
2022-06-08 18:04:06 -07:00
|
|
|
|
let txt = fs::read_to_string(format!("README{}.md", language.suffix()))?;
|
2022-05-03 23:05:55 -07:00
|
|
|
|
|
2022-06-12 18:02:09 -07:00
|
|
|
|
let mut chapters = vec![Chapter {
|
|
|
|
|
level: HeadingLevel::H1,
|
|
|
|
|
events: Vec::new(),
|
|
|
|
|
index: 0,
|
|
|
|
|
language,
|
|
|
|
|
}];
|
2022-05-03 23:05:55 -07:00
|
|
|
|
|
2022-06-08 18:04:06 -07:00
|
|
|
|
for event in Parser::new_ext(&txt, Options::all()) {
|
2022-06-12 18:02:09 -07:00
|
|
|
|
if let Event::Start(Tag::Heading(level @ (HeadingLevel::H2 | HeadingLevel::H3), ..)) = event {
|
|
|
|
|
let index = chapters.last().unwrap().index + 1;
|
|
|
|
|
chapters.push(Chapter {
|
|
|
|
|
level,
|
|
|
|
|
events: Vec::new(),
|
|
|
|
|
index,
|
|
|
|
|
language,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
chapters.last_mut().unwrap().events.push(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut links = BTreeMap::new();
|
|
|
|
|
|
|
|
|
|
for chapter in &chapters {
|
|
|
|
|
let mut current = None;
|
|
|
|
|
for event in &chapter.events {
|
|
|
|
|
match event {
|
|
|
|
|
Event::Start(Tag::Heading(..)) => current = Some(Vec::new()),
|
|
|
|
|
Event::End(Tag::Heading(level, ..)) => {
|
|
|
|
|
let events = current.unwrap();
|
|
|
|
|
let title = events
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|event| match event {
|
|
|
|
|
Event::Code(content) | Event::Text(content) => Some(content.deref()),
|
|
|
|
|
_ => None,
|
|
|
|
|
})
|
|
|
|
|
.collect::<String>();
|
|
|
|
|
let slug = slug(&title);
|
|
|
|
|
let link = if let HeadingLevel::H1 | HeadingLevel::H2 | HeadingLevel::H3 = level {
|
|
|
|
|
format!("chapter_{}.html", chapter.number())
|
|
|
|
|
} else {
|
|
|
|
|
format!("chapter_{}.html#{}", chapter.number(), slug)
|
|
|
|
|
};
|
|
|
|
|
links.insert(slug, link);
|
|
|
|
|
current = None;
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
if let Some(events) = &mut current {
|
|
|
|
|
events.push(event.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for chapter in &mut chapters {
|
|
|
|
|
for event in &mut chapter.events {
|
|
|
|
|
if let Event::Start(Tag::Link(_, dest, _)) | Event::End(Tag::Link(_, dest, _)) = event {
|
|
|
|
|
if let Some(anchor) = dest.clone().strip_prefix('#') {
|
|
|
|
|
*dest = CowStr::Borrowed(&links[anchor]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-08 18:04:06 -07:00
|
|
|
|
}
|
2022-05-03 23:05:55 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-08 18:04:06 -07:00
|
|
|
|
let mut summary = String::new();
|
|
|
|
|
|
2022-06-12 18:02:09 -07:00
|
|
|
|
for chapter in chapters {
|
|
|
|
|
let path = format!("{}/chapter_{}.md", src, chapter.number());
|
2022-12-15 16:53:21 -08:00
|
|
|
|
fs::write(path, chapter.markdown()?)?;
|
2022-06-12 18:02:09 -07:00
|
|
|
|
let indent = match chapter.level {
|
|
|
|
|
HeadingLevel::H1 => 0,
|
|
|
|
|
HeadingLevel::H2 => 1,
|
|
|
|
|
HeadingLevel::H3 => 2,
|
|
|
|
|
HeadingLevel::H4 => 3,
|
|
|
|
|
HeadingLevel::H5 => 4,
|
|
|
|
|
HeadingLevel::H6 => 5,
|
2022-06-08 18:04:06 -07:00
|
|
|
|
};
|
2022-09-11 02:25:38 -07:00
|
|
|
|
writeln!(
|
|
|
|
|
summary,
|
|
|
|
|
"{}- [{}](chapter_{}.md)",
|
2022-06-12 18:02:09 -07:00
|
|
|
|
" ".repeat(indent * 4),
|
|
|
|
|
chapter.title(),
|
|
|
|
|
chapter.number()
|
2022-09-11 02:25:38 -07:00
|
|
|
|
)?;
|
2022-06-08 18:04:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-26 18:49:03 -08:00
|
|
|
|
fs::write(format!("{src}/SUMMARY.md"), summary).unwrap();
|
2022-06-08 18:04:06 -07:00
|
|
|
|
}
|
2022-05-03 23:05:55 -07:00
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|