just/bin/generate-book/src/main.rs
Casey Rodarmor a81b094441
Downgrade to TLS 1.2 in install script (#1536)
Revert "Update install script and readmes to use tls v1.3 (#1481)"

This reverts commit 9b6b0b7fac.
2023-01-27 02:49:03 +00:00

191 lines
4.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use {
pulldown_cmark::{CowStr, Event, HeadingLevel, Options, Parser, Tag},
pulldown_cmark_to_cmark::cmark,
std::{collections::BTreeMap, error::Error, fmt::Write, fs, ops::Deref},
};
type Result<T = ()> = std::result::Result<T, Box<dyn Error>>;
#[derive(Copy, Clone, Debug)]
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 => ".中文",
}
}
fn introduction(&self) -> &'static str {
match self {
Self::Chinese => "说明",
Self::English => "Introduction",
}
}
}
#[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 {
for language in [Language::English, Language::Chinese] {
let src = format!("book/{}/src", language.code());
fs::remove_dir_all(&src).ok();
fs::create_dir(&src)?;
let txt = fs::read_to_string(format!("README{}.md", language.suffix()))?;
let mut chapters = vec![Chapter {
level: HeadingLevel::H1,
events: Vec::new(),
index: 0,
language,
}];
for event in Parser::new_ext(&txt, Options::all()) {
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]);
}
}
}
}
let mut summary = String::new();
for chapter in chapters {
let path = format!("{}/chapter_{}.md", src, chapter.number());
fs::write(path, chapter.markdown()?)?;
let indent = match chapter.level {
HeadingLevel::H1 => 0,
HeadingLevel::H2 => 1,
HeadingLevel::H3 => 2,
HeadingLevel::H4 => 3,
HeadingLevel::H5 => 4,
HeadingLevel::H6 => 5,
};
writeln!(
summary,
"{}- [{}](chapter_{}.md)",
" ".repeat(indent * 4),
chapter.title(),
chapter.number()
)?;
}
fs::write(format!("{src}/SUMMARY.md"), summary).unwrap();
}
Ok(())
}