106 lines
2.8 KiB
Rust
106 lines
2.8 KiB
Rust
use std::fmt::Write;
|
|
use std::fs::{self};
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
use std::str::FromStr;
|
|
|
|
use anyhow::{anyhow, Context};
|
|
use html::render_html;
|
|
use metadata::PageMetadata;
|
|
|
|
mod html;
|
|
mod metadata;
|
|
mod template;
|
|
|
|
fn copy_recursively(from: &Path, to: &Path) -> anyhow::Result<()> {
|
|
Command::new("cp").arg("-r").arg(from).arg(to).output()?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// {{{ Generate single page
|
|
fn generate_page(path: &Path) -> anyhow::Result<()> {
|
|
let content_path = PathBuf::from_str("content")?.join(path);
|
|
|
|
let djot_input = std::fs::read_to_string(&content_path).unwrap();
|
|
let mut out = String::new();
|
|
|
|
let mut page_renderer = template!("templates/page.html", &mut out)?;
|
|
|
|
let events = jotdown::Parser::new(&djot_input);
|
|
let metadata = PageMetadata::new(events, content_path)?;
|
|
|
|
while let Some(label) = page_renderer.current() {
|
|
if label == "content" {
|
|
let events = jotdown::Parser::new(&djot_input);
|
|
render_html(&metadata, events, &mut out)?;
|
|
page_renderer.next(&mut out)?;
|
|
} else if label == "navigation" {
|
|
out.write_str(r#"<a href="/"><code>~</code></a>"#)?;
|
|
out.write_str(" / ")?;
|
|
out.write_str(r#"<a href="/posts"><code>posts</code></a>"#)?;
|
|
page_renderer.next(&mut out)?;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
page_renderer.finish(&mut out)?;
|
|
|
|
let mut out_path = PathBuf::from_str("dist")?.join(path);
|
|
out_path.set_file_name(format!(
|
|
"{}.html",
|
|
path.file_stem()
|
|
.ok_or_else(|| anyhow!("Empty filestem encountered"))?
|
|
.to_str()
|
|
.unwrap()
|
|
));
|
|
std::fs::write(out_path, out).with_context(|| "Failed to write `arcaea.html` post")?;
|
|
|
|
Ok(())
|
|
}
|
|
// }}}
|
|
// {{{ Generate an entire directory
|
|
fn generate_dir(path: &Path) -> anyhow::Result<()> {
|
|
let content_path = PathBuf::from_str("content")?.join(path);
|
|
let out_path = PathBuf::from_str("dist")?.join(path);
|
|
fs::create_dir_all(&out_path)
|
|
.with_context(|| format!("Could not generate directory {path:?}"))?;
|
|
|
|
for file in fs::read_dir(&content_path)? {
|
|
let file_path = file?.path();
|
|
let filename = file_path.file_name().unwrap();
|
|
let path = path.join(filename);
|
|
if file_path.is_dir() {
|
|
generate_dir(&path)?;
|
|
} else if file_path.extension().map_or(false, |ext| ext == "dj") {
|
|
generate_page(&path)?;
|
|
} else {
|
|
fs::copy(content_path.join(filename), out_path.join(filename))?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
// }}}
|
|
|
|
fn main() -> anyhow::Result<()> {
|
|
let public_path = PathBuf::from_str("public")?;
|
|
let dist_path = PathBuf::from_str("dist")?;
|
|
|
|
if dist_path.exists() {
|
|
std::fs::remove_dir_all(&dist_path).with_context(|| "Cannot delete `dist` directory")?;
|
|
}
|
|
|
|
std::fs::create_dir(&dist_path).with_context(|| "Cannot create `dist` directory")?;
|
|
|
|
for p in std::fs::read_dir(public_path)? {
|
|
copy_recursively(&p?.path(), &dist_path)
|
|
.with_context(|| "Cannot copy `public` -> `dist`")?;
|
|
}
|
|
|
|
generate_dir(&PathBuf::new())?;
|
|
|
|
Ok(())
|
|
}
|