From 364059abef99c04e5210fb1ee437a3ec5bb67bcb Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Fri, 26 Apr 2024 14:40:44 +0200 Subject: [PATCH] init --- .gitignore | 3 + Cargo.lock | 7 +++ Cargo.toml | 8 +++ README.md | 12 ++++ rustfmt.toml | 1 + src/main.rs | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 197 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 rustfmt.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9624d02 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/write +/site diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5ea6197 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "htmd" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..77b1bed --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "htmd" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..53f4661 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# static site generator + +all files get copied from the `write` dir to the `site` dir +md files get converted to html in the process +using `template.html`, which should include `TITLE` and `CONTENT` +utf-8 is required + +/blog/post_1.md -> /blog/post_1/index.html +/blog/ferret.mp4 -> /blog/post_1/ferret.mp4 + +/blog/post_2/post_2.md -> /blog/post_2/index.html +/blog/post_2/cat.png -> /blog/post_2/cat.png diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..174c815 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,166 @@ +use std::{ + error::Error, + ffi::OsStr, + fs::{read_dir, read_to_string, File}, + io::Write, + path::{Path, PathBuf}, +}; + +const SRC_DIR: &str = "write"; +const OUT_DIR: &str = "site"; +type Result = core::result::Result<(), Box>; + +fn main() -> Result { + let src_dir = PathBuf::from(SRC_DIR); + build_dir(&src_dir) +} + +fn build_dir(dir: &Path) -> Result { + for entry in read_dir(dir).unwrap().flatten() { + let ftype = entry.file_type()?; + if ftype.is_dir() { + build_dir(&entry.path())?; + } else if ftype.is_file() { + build_file(&entry.path())?; + } + } + Ok(()) +} + +fn build_file(path: &Path) -> Result { + if path.extension().and_then(OsStr::to_str) == Some("md") { + convert_file(path) + } else { + todo!("move file") + } +} + +#[derive(Debug, Clone, PartialEq)] +enum S { + None, + P, + Code, + UList, + OList, +} + +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"; + state = S::None; + continue; + } else { + 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!("{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 template = read_to_string("template.html")?; + let html = template.replace("CONTENT HERE", &html); + + let mut file = File::create(out_path)?; + file.write_all(html.as_bytes())?; + + 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!("") + } + }; + + let mut chars = source.chars().peekable(); + while let Some(c) = chars.next() { + if c == '*' { + if chars.peek() == Some(&'*') { + _ = chars.next(); + is_b = !is_b; + // out += b_tag(); + out += &toggle(is_b, "strong"); + } else { + is_em = !is_em; + out += &toggle(is_em, "em"); + // out += if is_em { "" } else { "" }; + } + } else if c == '`' { + is_code = !is_code; + out += &toggle(is_code, "code"); + + // out += if is_code { "" } else { "" }; + } else if c == '_' { + is_ul = !is_ul; + out += &toggle(is_ul, "u"); + + // out += if is_ul { "" } else { "" }; + } else { + out.push(c); + } + } + 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 +}