This commit is contained in:
Crispy 2024-04-26 14:40:44 +02:00
commit 364059abef
6 changed files with 197 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
/write
/site

7
Cargo.lock generated Normal file
View file

@ -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"

8
Cargo.toml Normal file
View file

@ -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]

12
README.md Normal file
View file

@ -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

1
rustfmt.toml Normal file
View file

@ -0,0 +1 @@
hard_tabs = true

166
src/main.rs Normal file
View file

@ -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<dyn Error>>;
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 += "</pre>\n";
state = S::None;
continue;
} 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";
}
}
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!("</{tag}>")
}
};
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 { "<em>" } else { "</em>" };
}
} else if c == '`' {
is_code = !is_code;
out += &toggle(is_code, "code");
// out += if is_code { "<code>" } else { "</code>" };
} else if c == '_' {
is_ul = !is_ul;
out += &toggle(is_ul, "u");
// out += if is_ul { "<u>" } else { "</u>" };
} 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
}