diff --git a/src/html/home.rs b/src/html/home.rs index 0c39503..3030f4b 100644 --- a/src/html/home.rs +++ b/src/html/home.rs @@ -1,8 +1,8 @@ use camino::Utf8Path; -use hauchiwa::{Link, LinkDate, Sack}; +use hauchiwa::Sack; use hypertext::{html_elements, maud, maud_move, GlobalAttributes, Raw, Renderable}; -use crate::{html::Post, text::md::parse}; +use crate::{html::Post, text::md::parse, Link, LinkDate}; const INTRO: &str = r#" ## かもし diff --git a/src/html/list.rs b/src/html/list.rs index 46ab536..1fdfdce 100644 --- a/src/html/list.rs +++ b/src/html/list.rs @@ -1,7 +1,7 @@ -use hauchiwa::{LinkDate, Sack}; +use hauchiwa::Sack; use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable}; -use crate::html::page; +use crate::{html::page, LinkDate}; pub fn list<'s, 'g, 'html>( sack: &'s Sack, diff --git a/src/html/misc.rs b/src/html/misc.rs index 2565fed..0d54ce1 100644 --- a/src/html/misc.rs +++ b/src/html/misc.rs @@ -1,6 +1,10 @@ -use hauchiwa::{Outline, Sack, TreePage}; +use std::collections::HashMap; + +use camino::Utf8Path; +use hauchiwa::{Outline, Sack}; use hypertext::{html_elements, maud_move, GlobalAttributes, Raw, Renderable}; +use crate::{html::Wiki, Link}; /// Render the outline for a document pub(crate) fn show_outline(outline: Outline) -> impl Renderable { @@ -41,9 +45,50 @@ pub(crate) fn emit_bibliography(bib: Vec) -> impl Renderable { ) } +#[derive(Debug)] +pub struct TreePage { + pub link: Option, + pub subs: HashMap, +} + +impl TreePage { + fn new() -> Self { + TreePage { + link: None, + subs: HashMap::new(), + } + } + + fn add_link(&mut self, link: &Link) { + let mut ptr = self; + for part in link.path.iter().skip(1) { + ptr = ptr.subs.entry(part.to_string()).or_insert(TreePage::new()); + } + ptr.link = Some(link.clone()); + } + + fn from_iter(iter: impl Iterator) -> Self { + let mut tree = Self::new(); + for link in iter { + tree.add_link(&link); + } + + tree + } +} + /// Render the page tree pub(crate) fn show_page_tree(sack: &Sack, glob: &str) -> impl Renderable { - let tree = sack.get_tree(glob); + let tree = + TreePage::from_iter( + sack.get_meta::(glob) + .into_iter() + .map(|(path, meta)| Link { + path: Utf8Path::new("/").join(path), + name: meta.title.clone(), + desc: None, + }), + ); maud_move!( h2 .link-tree__heading { diff --git a/src/html/mod.rs b/src/html/mod.rs index 721b343..690d881 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -9,19 +9,19 @@ pub mod wiki; use std::collections::HashMap; -use camino::{Utf8Path, Utf8PathBuf}; -use chrono::{DateTime, Datelike, Utc}; -use hauchiwa::{Bibliography, Link, LinkDate, Linkable, Outline, Sack}; -use hayagriva::Library; +use camino::Utf8Path; +use chrono::Datelike; +use hauchiwa::{Bibliography, Outline, Sack}; use hypertext::{html_elements, maud, maud_move, GlobalAttributes, Raw, Renderable}; pub(crate) use home::home; use post::article; pub(crate) use post::Post; -use serde::Deserialize; pub(crate) use slideshow::Slideshow; pub(crate) use wiki::Wiki; +use crate::LinkDate; + fn navbar() -> impl Renderable { static ITEMS: &[(&str, &str)] = &[ ("Posts", "/posts/"), @@ -198,7 +198,7 @@ where ) } -pub(crate) fn search<'s, 'html>(sack: &'s Sack) -> String { +pub(crate) fn search(sack: &Sack) -> String { page( sack, maud!( @@ -222,7 +222,7 @@ pub fn as_html( flox(&meta.title, parsed, sack, outline, bibliography) } -pub(crate) fn flox<'p, 's, 'html>( +pub(crate) fn flox( title: &str, parsed: &str, sack: &Sack, diff --git a/src/html/post.rs b/src/html/post.rs index 5bc72cd..8da59a3 100644 --- a/src/html/post.rs +++ b/src/html/post.rs @@ -1,6 +1,6 @@ -use camino::{Utf8Path, Utf8PathBuf}; +use camino::Utf8Path; use chrono::{DateTime, Utc}; -use hauchiwa::{Bibliography, Link, LinkDate, Linkable, Outline, Sack}; +use hauchiwa::{Bibliography, Outline, Sack}; use hayagriva::Library; use hypertext::{html_elements, maud_move, GlobalAttributes, Raw, Renderable}; use serde::Deserialize; @@ -8,103 +8,92 @@ use serde::Deserialize; /// Represents a simple post. #[derive(Deserialize, Debug, Clone)] pub struct Post { - pub title: String, - #[serde(with = "super::isodate")] - pub date: DateTime, - pub desc: Option, - pub scripts: Option>, + pub title: String, + #[serde(with = "super::isodate")] + pub date: DateTime, + pub desc: Option, + pub scripts: Option>, } pub fn parse_content( - content: &str, - sack: &Sack, - path: &Utf8Path, - library: Option<&Library>, + content: &str, + sack: &Sack, + path: &Utf8Path, + library: Option<&Library>, ) -> (String, Outline, Bibliography) { - crate::text::md::parse(content, sack, path, library) + crate::text::md::parse(content, sack, path, library) } pub fn as_html( - meta: &Post, - parsed: &str, - sack: &Sack, - outline: Outline, - bibliography: Bibliography, + meta: &Post, + parsed: &str, + sack: &Sack, + outline: Outline, + bibliography: Bibliography, ) -> String { - post(meta, parsed, sack, outline, bibliography) - .unwrap() - .render() - .into() -} - -pub fn as_link(meta: &Post, path: Utf8PathBuf) -> Option { - Some(Linkable::Date(LinkDate { - link: Link { - path, - name: meta.title.to_owned(), - desc: meta.desc.to_owned(), - }, - date: meta.date.to_owned(), - })) + post(meta, parsed, sack, outline, bibliography) + .unwrap() + .render() + .into() } pub fn post<'s, 'p, 'html>( - meta: &'p Post, - parsed: &'p str, - sack: &'s Sack, - outline: Outline, - bibliography: Bibliography, + meta: &'p Post, + parsed: &'p str, + sack: &'s Sack, + outline: Outline, + bibliography: Bibliography, ) -> Result where - 's: 'html, - 'p: 'html, + 's: 'html, + 'p: 'html, { - let main = maud_move!( - main { - (article(&meta.title, parsed, sack, outline, bibliography)) - } - ); + let main = maud_move!( + main { + (article(&meta.title, parsed, sack, outline, bibliography)) + } + ); - crate::html::page(sack, main, meta.title.clone(), meta.scripts.as_deref()) + crate::html::page(sack, main, meta.title.clone(), meta.scripts.as_deref()) } pub fn article<'p, 's, 'html>( - title: &'p str, - parsed: &'p str, - _: &'s Sack, - outline: Outline, - bibliography: Bibliography, + title: &'p str, + parsed: &'p str, + _: &'s Sack, + outline: Outline, + bibliography: Bibliography, ) -> impl Renderable + 'html where - 's: 'html, - 'p: 'html, + 's: 'html, + 'p: 'html, { - maud_move!( - div .wiki-main { + maud_move!( + div .wiki-main { - // Slide in/out for mobile - input #wiki-aside-shown type="checkbox" hidden; + // Slide in/out for mobile + input #wiki-aside-shown type="checkbox" hidden; - aside .wiki-aside { - // Slide button - label .wiki-aside__slider for="wiki-aside-shown" { - img .wiki-icon src="/static/svg/double-arrow.svg" width="24" height="24"; - } - (crate::html::misc::show_outline(outline)) - } + aside .wiki-aside { + // Slide button + label .wiki-aside__slider for="wiki-aside-shown" { + img .wiki-icon src="/static/svg/double-arrow.svg" width="24" height="24"; + } + (crate::html::misc::show_outline(outline)) + } - article .wiki-article /*class:list={classlist)*/ { - header class="markdown" { - h1 #top { (title) } - } - section .wiki-article__markdown.markdown { - (Raw(parsed)) - } + article .wiki-article /*class:list={classlist)*/ { + header class="markdown" { + h1 #top { (title) } + } + section .wiki-article__markdown.markdown { + (Raw(parsed)) + } - @if let Some(bib) = bibliography.0 { - (crate::html::misc::emit_bibliography(bib)) - } - } - } - ) + @if let Some(bib) = bibliography.0 { + (crate::html::misc::emit_bibliography(bib)) + } + } + } + ) } diff --git a/src/html/slideshow.rs b/src/html/slideshow.rs index 49af0be..ffd46f6 100644 --- a/src/html/slideshow.rs +++ b/src/html/slideshow.rs @@ -1,6 +1,6 @@ -use camino::{Utf8Path, Utf8PathBuf}; +use camino::Utf8Path; use chrono::{DateTime, Utc}; -use hauchiwa::{Bibliography, Link, LinkDate, Linkable, Outline, Sack}; +use hauchiwa::{Bibliography, Outline, Sack}; use hayagriva::Library; use hypertext::{html_elements, maud, GlobalAttributes, Raw, Renderable}; use serde::Deserialize; @@ -16,77 +16,66 @@ const CSS: &str = r#" /// Represents a slideshow #[derive(Deserialize, Debug, Clone)] pub(crate) struct Slideshow { - pub title: String, - #[serde(with = "super::isodate")] - pub date: DateTime, - pub desc: Option, + pub title: String, + #[serde(with = "super::isodate")] + pub date: DateTime, + pub desc: Option, } pub fn parse_content( - content: &str, - sack: &Sack, - path: &Utf8Path, - library: Option<&Library>, + content: &str, + sack: &Sack, + path: &Utf8Path, + library: Option<&Library>, ) -> (String, Outline, Bibliography) { - let parsed = content - .split("\n-----\n") - .map(|chunk| { - chunk - .split("\n---\n") - .map(|slide| crate::text::md::parse(&slide, sack, path, library).0) - .collect::>() - }) - .map(|stack| match stack.len() > 1 { - true => format!( - "
{}
", - stack - .into_iter() - .map(|slide| format!("
{slide}
")) - .collect::() - ), - false => format!("
{}
", stack[0]), - }) - .collect::(); - (parsed, Outline(vec![]), Bibliography(None)) + let parsed = content + .split("\n-----\n") + .map(|chunk| { + chunk + .split("\n---\n") + .map(|slide| crate::text::md::parse(slide, sack, path, library).0) + .collect::>() + }) + .map(|stack| match stack.len() > 1 { + true => format!( + "
{}
", + stack + .into_iter() + .map(|slide| format!("
{slide}
")) + .collect::() + ), + false => format!("
{}
", stack[0]), + }) + .collect::(); + (parsed, Outline(vec![]), Bibliography(None)) } pub fn as_html( - slides: &Slideshow, - parsed: &str, - sack: &Sack, - _: Outline, - _: Bibliography, + slides: &Slideshow, + parsed: &str, + sack: &Sack, + _: Outline, + _: Bibliography, ) -> String { - show(slides, sack, parsed) -} - -pub fn as_link(slides: &Slideshow, path: Utf8PathBuf) -> Option { - Some(Linkable::Date(LinkDate { - link: Link { - path, - name: slides.title.to_owned(), - desc: slides.desc.to_owned(), - }, - date: slides.date.to_owned(), - })) + show(slides, sack, parsed) } pub fn show(fm: &Slideshow, sack: &Sack, slides: &str) -> String { - crate::html::bare( - sack, - maud!( - div .reveal { - div .slides { - (Raw(slides)) - } - } + crate::html::bare( + sack, + maud!( + div .reveal { + div .slides { + (Raw(slides)) + } + } - style { (Raw(CSS)) } - ), - fm.title.clone(), - Some(&["reveal".into()]), - ) - .unwrap() - .render() - .into() + style { (Raw(CSS)) } + ), + fm.title.clone(), + Some(&["reveal".into()]), + ) + .unwrap() + .render() + .into() } diff --git a/src/html/wiki.rs b/src/html/wiki.rs index 6435ca5..ed6bdbd 100644 --- a/src/html/wiki.rs +++ b/src/html/wiki.rs @@ -1,5 +1,5 @@ -use camino::{Utf8Path, Utf8PathBuf}; -use hauchiwa::{Bibliography, Link, Linkable, Outline, Sack}; +use camino::Utf8Path; +use hauchiwa::{Bibliography, Outline, Sack}; use hayagriva::Library; use hypertext::{html_elements, maud_move, GlobalAttributes, Raw, Renderable}; use serde::Deserialize; @@ -29,14 +29,6 @@ pub fn as_html( wiki(meta, parsed, sack, outline, bibliography) } -pub fn as_link(meta: &Wiki, path: Utf8PathBuf) -> Option { - Some(Linkable::Link(Link { - path, - name: meta.title.to_owned(), - desc: None, - })) -} - fn wiki( matter: &Wiki, parsed: &str, diff --git a/src/main.rs b/src/main.rs index 56d9020..61173c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,10 @@ mod html; mod text; mod ts; -use camino::Utf8Path; +use camino::{Utf8Path, Utf8PathBuf}; +use chrono::{DateTime, Utc}; use clap::{Parser, ValueEnum}; -use hauchiwa::{Collection, Link, LinkDate, Processor, Website}; +use hauchiwa::{Collection, Processor, Website}; use html::{Post, Slideshow, Wiki}; use hypertext::Renderable; @@ -20,6 +21,25 @@ enum Mode { Watch, } +#[derive(Debug, Clone)] +pub struct Link { + pub path: Utf8PathBuf, + pub name: String, + pub desc: Option, +} + +#[derive(Debug, Clone)] +pub struct LinkDate { + pub link: Link, + pub date: DateTime, +} + +#[derive(Debug, Clone)] +pub enum Linkable { + Link(Link), + Date(LinkDate), +} + fn main() { let args = Args::parse(); @@ -32,7 +52,6 @@ fn main() { Processor { read_content: crate::html::post::parse_content, to_html: crate::html::post::as_html, - to_link: crate::html::post::as_link, }, ), Collection::glob_with::( @@ -42,7 +61,6 @@ fn main() { Processor { read_content: crate::html::post::parse_content, to_html: crate::html::post::as_html, - to_link: crate::html::post::as_link, }, ), Collection::glob_with::( @@ -52,7 +70,6 @@ fn main() { Processor { read_content: crate::html::slideshow::parse_content, to_html: crate::html::slideshow::as_html, - to_link: crate::html::slideshow::as_link, }, ), Collection::glob_with::( @@ -62,7 +79,6 @@ fn main() { Processor { read_content: crate::html::wiki::parse_content, to_html: crate::html::wiki::as_html, - to_link: crate::html::wiki::as_link, }, ), Collection::glob_with::( @@ -72,7 +88,6 @@ fn main() { Processor { read_content: crate::html::post::parse_content, to_html: crate::html::as_html, - to_link: crate::html::post::as_link, }, ), ]) @@ -85,7 +100,7 @@ fn main() { |sack| crate::html::map(sack).unwrap().render().to_owned().into(), "map/index.html".into(), ) - .add_virtual(|sack| crate::html::search(sack), "search/index.html".into()) + .add_virtual(crate::html::search, "search/index.html".into()) .add_virtual( |sack| { crate::html::to_list( diff --git a/src/text/md.rs b/src/text/md.rs index 5a581a9..771c4f5 100644 --- a/src/text/md.rs +++ b/src/text/md.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; -use camino::{Utf8Path, Utf8PathBuf}; +use camino::Utf8Path; use hauchiwa::{Bibliography, Outline, Sack}; use hayagriva::{ - archive::ArchivedStyle, - citationberg::{IndependentStyle, Locale, Style}, - BibliographyDriver, BibliographyRequest, BufWriteFormat, CitationItem, CitationRequest, - Library, + archive::ArchivedStyle, + citationberg::{IndependentStyle, Locale, Style}, + BibliographyDriver, BibliographyRequest, BufWriteFormat, CitationItem, CitationRequest, + Library, }; use hypertext::Renderable; use once_cell::sync::Lazy; @@ -16,369 +16,364 @@ use regex::Regex; use crate::ts; static OPTS: Lazy = Lazy::new(|| { - Options::empty() - .union(Options::ENABLE_MATH) - .union(Options::ENABLE_TABLES) - .union(Options::ENABLE_TASKLISTS) - .union(Options::ENABLE_STRIKETHROUGH) - .union(Options::ENABLE_SMART_PUNCTUATION) + Options::empty() + .union(Options::ENABLE_MATH) + .union(Options::ENABLE_TABLES) + .union(Options::ENABLE_TASKLISTS) + .union(Options::ENABLE_STRIKETHROUGH) + .union(Options::ENABLE_SMART_PUNCTUATION) }); static KATEX_I: Lazy = Lazy::new(|| { - katex::opts::Opts::builder() - .output_type(katex::OutputType::Mathml) - .build() - .unwrap() + katex::opts::Opts::builder() + .output_type(katex::OutputType::Mathml) + .build() + .unwrap() }); static KATEX_B: Lazy = Lazy::new(|| { - katex::opts::Opts::builder() - .output_type(katex::OutputType::Mathml) - .display_mode(true) - .build() - .unwrap() + katex::opts::Opts::builder() + .output_type(katex::OutputType::Mathml) + .display_mode(true) + .build() + .unwrap() }); static LOCALE: Lazy> = Lazy::new(hayagriva::archive::locales); static STYLE: Lazy = - Lazy::new( - || match ArchivedStyle::InstituteOfElectricalAndElectronicsEngineers.get() { - Style::Independent(style) => style, - Style::Dependent(_) => unreachable!(), - }, - ); + Lazy::new( + || match ArchivedStyle::InstituteOfElectricalAndElectronicsEngineers.get() { + Style::Independent(style) => style, + Style::Dependent(_) => unreachable!(), + }, + ); pub fn parse( - content: &str, - sack: &Sack, - path: &Utf8Path, - library: Option<&Library>, + content: &str, + sack: &Sack, + path: &Utf8Path, + library: Option<&Library>, ) -> (String, Outline, Bibliography) { - let (outline, stream) = { - let stream = Parser::new_ext(content, *OPTS); - let mut stream: Vec<_> = TextMergeStream::new(stream).collect(); - let outline = set_heading_ids(&mut stream); - (outline, stream) - }; + let (outline, stream) = { + let stream = Parser::new_ext(content, *OPTS); + let mut stream: Vec<_> = TextMergeStream::new(stream).collect(); + let outline = set_heading_ids(&mut stream); + (outline, stream) + }; - let stream = stream - .into_iter() - .map(make_math) - .map(make_emoji) - .map(swap_hashed_image(path, sack)) - .collect::>(); + let stream = stream + .into_iter() + .map(make_math) + .map(make_emoji) + .map(swap_hashed_image(path, sack)) + .collect::>(); - let stream = make_code(stream) - .into_iter() - .flat_map(make_ruby) - .flat_map(make_cite) - .collect::>(); + let stream = make_code(stream) + .into_iter() + .flat_map(make_ruby) + .flat_map(make_cite) + .collect::>(); - let (stream, bib) = match library { - Some(lib) => make_bib(stream, lib), - None => (stream, None), - }; + let (stream, bib) = match library { + Some(lib) => make_bib(stream, lib), + None => (stream, None), + }; - let mut parsed = String::new(); - pulldown_cmark::html::push_html(&mut parsed, stream.into_iter()); + let mut parsed = String::new(); + pulldown_cmark::html::push_html(&mut parsed, stream.into_iter()); - (parsed, outline, Bibliography(bib)) + (parsed, outline, Bibliography(bib)) } -fn make_bib<'a, 'b>( - stream: Vec>, - lib: &'b Library, -) -> (Vec>, Option>) { - let mut driver = BibliographyDriver::new(); +fn make_bib<'a>(stream: Vec>, lib: &Library) -> (Vec>, Option>) { + let mut driver = BibliographyDriver::new(); - for event in stream.iter() { - match event { - Event::InlineMath(ref text) => match lib.get(text) { - Some(entry) => driver.citation(CitationRequest::from_items( - vec![CitationItem::with_entry(entry)], - &STYLE, - &LOCALE, - )), - None => (), - }, - _ => (), - } - } + for event in stream.iter() { + if let Event::InlineMath(ref text) = event { + if let Some(entry) = lib.get(text) { + driver.citation(CitationRequest::from_items( + vec![CitationItem::with_entry(entry)], + &STYLE, + &LOCALE, + )) + } + } + } - // add fake citation to make all entries show up - driver.citation(CitationRequest::from_items( - lib.iter().map(CitationItem::with_entry).collect(), - &STYLE, - &LOCALE, - )); + // add fake citation to make all entries show up + driver.citation(CitationRequest::from_items( + lib.iter().map(CitationItem::with_entry).collect(), + &STYLE, + &LOCALE, + )); - let res = driver.finish(BibliographyRequest { - style: &STYLE, - locale: None, - locale_files: &LOCALE, - }); + let res = driver.finish(BibliographyRequest { + style: &STYLE, + locale: None, + locale_files: &LOCALE, + }); - let mut n = 0; - let stream = stream - .into_iter() - .map(|event| match event { - Event::InlineMath(name) => { - let mut buffer = String::from(""); - match res.citations.get(n) { - Some(rf) => rf - .citation - .write_buf(&mut buffer, BufWriteFormat::Html) - .unwrap(), - None => buffer.push_str(&name), - }; - buffer.push_str(""); - n += 1; - Event::InlineHtml(buffer.into()) - } - _ => event, - }) - .collect(); + let mut n = 0; + let stream = stream + .into_iter() + .map(|event| match event { + Event::InlineMath(name) => { + let mut buffer = String::from(""); + match res.citations.get(n) { + Some(rf) => rf + .citation + .write_buf(&mut buffer, BufWriteFormat::Html) + .unwrap(), + None => buffer.push_str(&name), + }; + buffer.push_str(""); + n += 1; + Event::InlineHtml(buffer.into()) + } + _ => event, + }) + .collect(); - let bib = res.bibliography.map(|bib| { - bib.items - .iter() - .map(|x| { - let mut buffer = String::new(); - x.content - .write_buf(&mut buffer, BufWriteFormat::Html) - .unwrap(); - buffer - }) - .collect::>() - }); + let bib = res.bibliography.map(|bib| { + bib.items + .iter() + .map(|x| { + let mut buffer = String::new(); + x.content + .write_buf(&mut buffer, BufWriteFormat::Html) + .unwrap(); + buffer + }) + .collect::>() + }); - (stream, bib) + (stream, bib) } static RE_CITE: Lazy = Lazy::new(|| Regex::new(r":cite\[([^\]]+)\]").unwrap()); #[derive(Debug)] enum Annotated_<'a> { - Text(&'a str), - Cite(&'a str), + Text(&'a str), + Cite(&'a str), } fn annotate_(input: &str) -> Vec { - let mut parts: Vec = Vec::new(); - let mut last_index = 0; + let mut parts: Vec = Vec::new(); + let mut last_index = 0; - for cap in RE_CITE.captures_iter(input) { - let cite = cap.get(1).unwrap().as_str(); - let index = cap.get(0).unwrap().start(); + for cap in RE_CITE.captures_iter(input) { + let cite = cap.get(1).unwrap().as_str(); + let index = cap.get(0).unwrap().start(); - if index > last_index { - parts.push(Annotated_::Text(&input[last_index..index])); - } + if index > last_index { + parts.push(Annotated_::Text(&input[last_index..index])); + } - parts.push(Annotated_::Cite(cite)); - last_index = cap.get(0).unwrap().end(); - } + parts.push(Annotated_::Cite(cite)); + last_index = cap.get(0).unwrap().end(); + } - if last_index < input.len() { - parts.push(Annotated_::Text(&input[last_index..])); - } + if last_index < input.len() { + parts.push(Annotated_::Text(&input[last_index..])); + } - parts + parts } fn make_cite(event: Event) -> Vec { - match event { - Event::Text(ref text) => annotate_(text) - .into_iter() - .map(|e| match e { - Annotated_::Text(text) => Event::Text(text.to_owned().into()), - Annotated_::Cite(cite) => Event::InlineMath(cite.to_owned().into()), - }) - .collect(), - _ => vec![event], - } + match event { + Event::Text(ref text) => annotate_(text) + .into_iter() + .map(|e| match e { + Annotated_::Text(text) => Event::Text(text.to_owned().into()), + Annotated_::Cite(cite) => Event::InlineMath(cite.to_owned().into()), + }) + .collect(), + _ => vec![event], + } } fn set_heading_ids(events: &mut [Event]) -> Outline { - let mut cnt = HashMap::::new(); - let mut out = Vec::new(); - let mut buf = String::new(); - let mut ptr = None; + let mut cnt = HashMap::::new(); + let mut out = Vec::new(); + let mut buf = String::new(); + let mut ptr = None; - for event in events { - match event { - Event::Start(ref mut tag @ Tag::Heading { .. }) => { - ptr = Some(tag); - } - Event::Text(ref text) if ptr.is_some() => buf.push_str(text), - Event::End(TagEnd::Heading(..)) => { - let txt = std::mem::take(&mut buf); - let url = txt.to_lowercase().replace(' ', "-"); - let url = match cnt.get_mut(&url) { - Some(ptr) => { - *ptr += 1; - format!("{url}-{ptr}") - } - None => { - cnt.insert(url.clone(), 0); - url - } - }; - match ptr.take().unwrap() { - Tag::Heading { ref mut id, .. } => *id = Some(url.clone().into()), - _ => unreachable!(), - } - out.push((txt, url)); - } - _ => (), - } - } + for event in events { + match event { + Event::Start(ref mut tag @ Tag::Heading { .. }) => { + ptr = Some(tag); + } + Event::Text(ref text) if ptr.is_some() => buf.push_str(text), + Event::End(TagEnd::Heading(..)) => { + let txt = std::mem::take(&mut buf); + let url = txt.to_lowercase().replace(' ', "-"); + let url = match cnt.get_mut(&url) { + Some(ptr) => { + *ptr += 1; + format!("{url}-{ptr}") + } + None => { + cnt.insert(url.clone(), 0); + url + } + }; + match ptr.take().unwrap() { + Tag::Heading { ref mut id, .. } => *id = Some(url.clone().into()), + _ => unreachable!(), + } + out.push((txt, url)); + } + _ => (), + } + } - Outline(out) + Outline(out) } fn make_math(event: Event) -> Event { - match event { - Event::InlineMath(math) => { - Event::InlineHtml(katex::render_with_opts(&math, &*KATEX_I).unwrap().into()) - } - Event::DisplayMath(math) => { - Event::Html(katex::render_with_opts(&math, &*KATEX_B).unwrap().into()) - } - _ => event, - } + match event { + Event::InlineMath(math) => { + Event::InlineHtml(katex::render_with_opts(&math, &*KATEX_I).unwrap().into()) + } + Event::DisplayMath(math) => { + Event::Html(katex::render_with_opts(&math, &*KATEX_B).unwrap().into()) + } + _ => event, + } } fn make_code(es: Vec) -> Vec { - let mut buff = Vec::new(); - let mut lang = None; - let mut code = String::new(); + let mut buff = Vec::new(); + let mut lang = None; + let mut code = String::new(); - for event in es { - match event { - Event::Start(Tag::CodeBlock(kind)) => match kind { - CodeBlockKind::Indented => (), - CodeBlockKind::Fenced(name) => lang = Some(name), - }, - Event::End(TagEnd::CodeBlock) => { - let lang = lang.take().unwrap_or("".into()); - let html = ts::highlight(&lang, &code).render().as_str().to_owned(); - buff.push(Event::Html(html.into())); - code.clear(); - } - Event::Text(text) => match lang { - None => buff.push(Event::Text(text)), - Some(_) => code.push_str(&text), - }, - _ => buff.push(event), - } - } + for event in es { + match event { + Event::Start(Tag::CodeBlock(kind)) => match kind { + CodeBlockKind::Indented => (), + CodeBlockKind::Fenced(name) => lang = Some(name), + }, + Event::End(TagEnd::CodeBlock) => { + let lang = lang.take().unwrap_or("".into()); + let html = ts::highlight(&lang, &code).render().as_str().to_owned(); + buff.push(Event::Html(html.into())); + code.clear(); + } + Event::Text(text) => match lang { + None => buff.push(Event::Text(text)), + Some(_) => code.push_str(&text), + }, + _ => buff.push(event), + } + } - buff + buff } static RE_RUBY: Lazy = Lazy::new(|| Regex::new(r"\[([^\]]+)\]\{([^}]+)\}").unwrap()); #[derive(Debug)] enum Annotated<'a> { - Text(&'a str), - Ruby(&'a str, &'a str), + Text(&'a str), + Ruby(&'a str, &'a str), } fn annotate(input: &str) -> Vec { - let mut parts: Vec = Vec::new(); - let mut last_index = 0; + let mut parts: Vec = Vec::new(); + let mut last_index = 0; - for cap in RE_RUBY.captures_iter(input) { - let text = cap.get(1).unwrap().as_str(); - let ruby = cap.get(2).unwrap().as_str(); - let index = cap.get(0).unwrap().start(); + for cap in RE_RUBY.captures_iter(input) { + let text = cap.get(1).unwrap().as_str(); + let ruby = cap.get(2).unwrap().as_str(); + let index = cap.get(0).unwrap().start(); - if index > last_index { - parts.push(Annotated::Text(&input[last_index..index])); - } + if index > last_index { + parts.push(Annotated::Text(&input[last_index..index])); + } - parts.push(Annotated::Ruby(text, ruby)); - last_index = cap.get(0).unwrap().end(); - } + parts.push(Annotated::Ruby(text, ruby)); + last_index = cap.get(0).unwrap().end(); + } - if last_index < input.len() { - parts.push(Annotated::Text(&input[last_index..])); - } + if last_index < input.len() { + parts.push(Annotated::Text(&input[last_index..])); + } - parts + parts } fn make_ruby(event: Event) -> Vec { - match event { - Event::Text(ref text) => annotate(text) - .into_iter() - .map(|el| match el { - Annotated::Text(text) => Event::Text(text.to_owned().into()), - Annotated::Ruby(t, f) => Event::InlineHtml( - format!("{t}({f})").into(), - ), - }) - .collect(), - _ => vec![event], - } + match event { + Event::Text(ref text) => annotate(text) + .into_iter() + .map(|el| match el { + Annotated::Text(text) => Event::Text(text.to_owned().into()), + Annotated::Ruby(t, f) => Event::InlineHtml( + format!("{t}({f})").into(), + ), + }) + .collect(), + _ => vec![event], + } } fn make_emoji(event: Event) -> Event { - match event { - Event::Text(ref text) => { - let mut buf = None; - let mut top = 0; - let mut old = 0; + match event { + Event::Text(ref text) => { + let mut buf = None; + let mut top = 0; + let mut old = 0; - for (idx, _) in text.match_indices(':') { - let key = &text[old..idx]; + for (idx, _) in text.match_indices(':') { + let key = &text[old..idx]; - if let Some(emoji) = emojis::get_by_shortcode(key) { - let buf = buf.get_or_insert_with(|| String::with_capacity(text.len())); - buf.push_str(&text[top..old - 1]); - buf.push_str(emoji.as_str()); - top = idx + 1; - } + if let Some(emoji) = emojis::get_by_shortcode(key) { + let buf = buf.get_or_insert_with(|| String::with_capacity(text.len())); + buf.push_str(&text[top..old - 1]); + buf.push_str(emoji.as_str()); + top = idx + 1; + } - old = idx + 1; - } + old = idx + 1; + } - if let Some(ref mut buf) = buf { - buf.push_str(&text[top..]); - } + if let Some(ref mut buf) = buf { + buf.push_str(&text[top..]); + } - match buf { - None => event, - Some(buf) => Event::Text(buf.into()), - } - } - _ => event, - } + match buf { + None => event, + Some(buf) => Event::Text(buf.into()), + } + } + _ => event, + } } fn swap_hashed_image<'a>(dir: &'a Utf8Path, sack: &'a Sack) -> impl Fn(Event) -> Event + 'a { - move |event| match event { - Event::Start(start) => match start { - Tag::Image { - dest_url, - link_type, - title, - id, - } => { - let rel = dir.join(dest_url.as_ref()); - let img = sack.get_image(&rel); - let hashed = img.map(|path| path.as_str().to_owned().into()); - Event::Start(Tag::Image { - link_type, - dest_url: hashed.unwrap_or(dest_url), - title, - id, - }) - } - _ => Event::Start(start), - }, - _ => event, - } + move |event| match event { + Event::Start(start) => match start { + Tag::Image { + dest_url, + link_type, + title, + id, + } => { + let rel = dir.join(dest_url.as_ref()); + let img = sack.get_image(&rel); + let hashed = img.map(|path| path.as_str().to_owned().into()); + Event::Start(Tag::Image { + link_type, + dest_url: hashed.unwrap_or(dest_url), + title, + id, + }) + } + _ => Event::Start(start), + }, + _ => event, + } }