init
This commit is contained in:
commit
364059abef
6 changed files with 197 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
/write
|
||||
/site
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal 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
8
Cargo.toml
Normal 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
12
README.md
Normal 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
1
rustfmt.toml
Normal file
|
@ -0,0 +1 @@
|
|||
hard_tabs = true
|
166
src/main.rs
Normal file
166
src/main.rs
Normal 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
|
||||
}
|
Loading…
Reference in a new issue