Compare commits

...

3 commits

Author SHA1 Message Date
839fd164aa ignore non-md files 2024-04-26 23:23:33 +02:00
07daad708b add filename to template title 2024-04-26 23:14:06 +02:00
b89934d3ac cleanup 2024-04-26 23:03:00 +02:00
3 changed files with 162 additions and 150 deletions

146
src/convert.rs Normal file
View file

@ -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 += "</pre>\n";
state = S::None;
} else {
if state == S::P {
html += "</p>\n";
}
state = S::Code;
html += "<pre>\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 += "</p>\n";
}
let header = &convert_line(header);
html += &format!("<h{level}>{header}</h{level}>\n");
continue;
}
}
if state == S::P && line.is_empty() {
state = S::None;
html += "</p>\n";
} else if !line.is_empty() {
if state == S::None {
state = S::P;
html += "<p>\n";
}
html += &convert_line(line);
html += "<br>\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<String>)> = 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!("<a href=\"{href}\">{link_text}</a>");
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 += "</em>";
}
if is_b {
out += "</strong>";
}
if is_code {
out += "</code>";
}
if is_ul {
out += "</u>";
}
out
}

View file

@ -6,9 +6,13 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
mod convert;
use convert::convert_document;
const SRC_DIR: &str = "write"; const SRC_DIR: &str = "write";
const OUT_DIR: &str = "site"; const OUT_DIR: &str = "site";
const CONTENT_MARKER: &str = "CONTENT HERE"; const CONTENT_MARKER: &str = "{CONTENT}";
const FILENAME_MARKER: &str = "{FILENAME}";
const TEMPLATE_FILE: &str = "template.html"; const TEMPLATE_FILE: &str = "template.html";
const DEFAULT_TEMPLATE: &[u8] = include_bytes!("../template.html"); const DEFAULT_TEMPLATE: &[u8] = include_bytes!("../template.html");
@ -49,74 +53,23 @@ fn build_file(path: &Path) -> Result {
if path.extension().and_then(OsStr::to_str) == Some("md") { if path.extension().and_then(OsStr::to_str) == Some("md") {
convert_file(path) convert_file(path)
} else { } else {
todo!("move file") println!("file ignored: {}", path.display());
// TODO copy non dotfiles
Ok(())
} }
} }
#[derive(Debug, Clone, PartialEq)]
enum S {
None,
P,
Code,
}
fn convert_file(path: &Path) -> Result { 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 = PathBuf::from(OUT_DIR).join(path.strip_prefix(SRC_DIR)?);
let out_path = out_path.with_extension("html"); let out_path = out_path.with_extension("html");
let mut html = String::new(); let markdown = read_to_string(path)?;
let mut state = S::None; let content = convert_document(&markdown);
for line in markdown.lines() {
if line.starts_with("```") {
if state == S::Code {
html += "</pre>\n";
state = S::None;
continue;
}
if state == S::P {
html += "</p>\n";
}
state = S::Code;
html += "<pre>\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 += "</p>\n";
}
let header = &convert_line(header);
html += &format!("<h{level}>{header}</h{level}>\n");
continue;
}
}
if state == S::P && line.is_empty() {
state = S::None;
html += "</p>\n";
} else if !line.is_empty() {
if state == S::None {
state = S::P;
html += "<p>\n";
}
html += &convert_line(line);
html += "<br>\n";
}
}
let template = read_to_string(TEMPLATE_FILE)?; let template = read_to_string(TEMPLATE_FILE)?;
let html = template.replacen(CONTENT_MARKER, &html, 1); let filename = path.file_stem().unwrap().to_string_lossy().to_string();
let html = template
.replacen(CONTENT_MARKER, &content, 1)
.replace(FILENAME_MARKER, &filename);
DirBuilder::new() DirBuilder::new()
.recursive(true) .recursive(true)
@ -126,90 +79,3 @@ fn convert_file(path: &Path) -> Result {
println!("built {}", out_path.display()); println!("built {}", out_path.display());
Ok(()) 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<String>)> = 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!("<a href=\"{href}\">{link_text}</a>");
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
}

View file

@ -4,11 +4,11 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TITLE HERE</title> <title>Title - {FILENAME}</title>
</head> </head>
<body> <body>
CONTENT HERE {CONTENT}
</body> </body>
</html> </html>