diff --git a/src/convert.rs b/src/convert.rs new file mode 100644 index 0000000..0a211e6 --- /dev/null +++ b/src/convert.rs @@ -0,0 +1,146 @@ +#[derive(Debug, Clone, PartialEq)] +enum S { + None, + P, + Code, +} + +pub fn convert_document(markdown: &str) -> String { + let mut html = String::new(); + let mut state = S::None; + + for line in markdown.lines() { + if line.starts_with("```") { + if state == S::Code { + html += "\n"; + state = S::None; + continue; + } + if state == S::P { + html += "
\n"; + } + state = S::Code; + html += "\n"; + continue; + } + + if state == S::Code { + html += line; + html += "\n"; + continue; + } + + if let Some((start, header)) = line.split_once(' ') { + let level = start.len(); + if (1..=6).contains(&level) && start.chars().all(|c| c == '#') { + if state == S::P { + state = S::None; + html += "\n"; + } + let header = &convert_line(header); + html += &format!("\n"; - state = S::None; - continue; - } - if state == S::P { - html += "\n"; - } - state = S::Code; - html += "{header} \n"); + continue; + } + } + + if state == S::P && line.is_empty() { + state = S::None; + html += "\n"; + } else if !line.is_empty() { + if state == S::None { + state = S::P; + html += "\n"; + } + html += &convert_line(line); + html += "
\n"; + } + } + html +} + +fn convert_line(source: &str) -> String { + let mut out = String::new(); + let mut is_em = false; + let mut is_b = false; + let mut is_code = false; + let mut is_ul = false; + let toggle = |state: bool, tag: &str| { + if state { + format!("<{tag}>") + } else { + format!("{tag}>") + } + }; + + let mut link: Option<(String, Option)> = None; + + let mut chars = source.chars().peekable(); + while let Some(c) = chars.next() { + if let Some(link_c) = &mut link { + match link_c { + (link_text, None) => { + if c == ']' { + if chars.peek() == Some(&'(') { + _ = chars.next(); + link_c.1 = Some(String::new()); + } else { + out += &format!("[{link_text}]"); + link = None; + } + } else { + link_text.push(c); + } + } + (link_text, Some(href)) => { + if c == ')' { + out += &format!("{link_text}"); + link = None; + } else { + href.push(c); + } + } + } + continue; + } + if c == '[' { + link = Some((String::new(), None)); + } else if c == '*' { + if chars.peek() == Some(&'*') { + _ = chars.next(); + is_b = !is_b; + out += &toggle(is_b, "strong"); + } else { + is_em = !is_em; + out += &toggle(is_em, "em"); + } + } else if c == '`' { + is_code = !is_code; + out += &toggle(is_code, "code"); + } else if c == '_' { + is_ul = !is_ul; + out += &toggle(is_ul, "u"); + } else { + out.push(c); + } + } + if let Some((link_text, href)) = link { + out += &format!("[{link_text}"); + if let Some(href) = href { + out += &format!("]({href}"); + } + } + + if is_em { + out += ""; + } + if is_b { + out += ""; + } + if is_code { + out += ""; + } + if is_ul { + out += ""; + } + out +} diff --git a/src/main.rs b/src/main.rs index 8681195..13add86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,9 @@ use std::{ path::{Path, PathBuf}, }; +mod convert; +use convert::convert_document; + const SRC_DIR: &str = "write"; const OUT_DIR: &str = "site"; const CONTENT_MARKER: &str = "CONTENT HERE"; @@ -53,67 +56,12 @@ fn build_file(path: &Path) -> Result { } } -#[derive(Debug, Clone, PartialEq)] -enum S { - None, - P, - Code, -} - fn convert_file(path: &Path) -> Result { - let markdown = read_to_string(path)?; let out_path = PathBuf::from(OUT_DIR).join(path.strip_prefix(SRC_DIR)?); let out_path = out_path.with_extension("html"); - let mut html = String::new(); - let mut state = S::None; - - for line in markdown.lines() { - if line.starts_with("```") { - if state == S::Code { - html += "
\n"; - continue; - } - - if state == S::Code { - html += line; - html += "\n"; - continue; - } - - if let Some((start, header)) = line.split_once(' ') { - let level = start.len(); - if (1..=6).contains(&level) && start.chars().all(|c| c == '#') { - if state == S::P { - state = S::None; - html += "\n"; - } - let header = &convert_line(header); - html += &format!("{header} \n"); - continue; - } - } - - if state == S::P && line.is_empty() { - state = S::None; - html += "\n"; - } else if !line.is_empty() { - if state == S::None { - state = S::P; - html += "\n"; - } - html += &convert_line(line); - html += "
\n"; - } - } + let markdown = read_to_string(path)?; + let html = convert_document(&markdown); let template = read_to_string(TEMPLATE_FILE)?; let html = template.replacen(CONTENT_MARKER, &html, 1); @@ -126,90 +74,3 @@ fn convert_file(path: &Path) -> Result { println!("built {}", out_path.display()); Ok(()) } - -fn convert_line(source: &str) -> String { - let mut out = String::new(); - let mut is_em = false; - let mut is_b = false; - let mut is_code = false; - let mut is_ul = false; - let toggle = |state: bool, tag: &str| { - if state { - format!("<{tag}>") - } else { - format!("{tag}>") - } - }; - - let mut link: Option<(String, Option)> = None; - - let mut chars = source.chars().peekable(); - while let Some(c) = chars.next() { - if let Some(link_c) = &mut link { - match link_c { - (link_text, None) => { - if c == ']' { - if chars.peek() == Some(&'(') { - _ = chars.next(); - link_c.1 = Some(String::new()); - } else { - out += &format!("[{link_text}]"); - link = None; - } - } else { - link_text.push(c); - } - } - (link_text, Some(href)) => { - if c == ')' { - out += &format!("{link_text}"); - link = None; - } else { - href.push(c); - } - } - } - continue; - } - if c == '[' { - link = Some((String::new(), None)); - } else if c == '*' { - if chars.peek() == Some(&'*') { - _ = chars.next(); - is_b = !is_b; - out += &toggle(is_b, "strong"); - } else { - is_em = !is_em; - out += &toggle(is_em, "em"); - } - } else if c == '`' { - is_code = !is_code; - out += &toggle(is_code, "code"); - } else if c == '_' { - is_ul = !is_ul; - out += &toggle(is_ul, "u"); - } else { - out.push(c); - } - } - if let Some((link_text, href)) = link { - out += &format!("[{link_text}"); - if let Some(href) = href { - out += &format!("]({href}"); - } - } - - if is_em { - out += &toggle(false, "em"); - } - if is_b { - out += &toggle(false, "b"); - } - if is_code { - out += &toggle(false, "code"); - } - if is_ul { - out += &toggle(false, "u"); - } - out -}