pub mod one_more; pub mod slugify; pub mod templates; pub mod word_count; use anyhow::anyhow; use askama::Template; use serde::Deserialize; use std::fs::File; use std::io::{BufWriter, Write}; use std::path::Path; pub fn output_writer(path: impl AsRef) -> Result { let path = crate::generator::output_path(path); if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; } let file = File::create(path)?; Ok(BufWriter::new(file)) } pub fn output_rendered_template( template: &impl Template, file: impl AsRef, ) -> Result<(), std::io::Error> { let path = file.as_ref(); let writer = output_writer(path)?; template.render_into(&mut FmtWriter(writer)).map_err(|e| { std::io::Error::new( std::io::ErrorKind::Other, format!("writing template {}: {}", path.display(), e), ) }) } struct FmtWriter(W); impl std::fmt::Write for FmtWriter { fn write_str(&mut self, s: &str) -> std::fmt::Result { self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error) } fn write_char(&mut self, c: char) -> std::fmt::Result { let mut buf = [0u8; 4]; c.encode_utf8(&mut buf); self.0.write_all(&buf).map_err(|_| std::fmt::Error) } fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::fmt::Result { self.0.write_fmt(args).map_err(|_| std::fmt::Error) } } pub fn from_frontmatter<'de, D: Deserialize<'de>>( contents: &'de str, ) -> anyhow::Result<(D, &'de str)> { let mut chars = contents.char_indices(); for i in 0..=2 { if chars.next() != Some((i, '`')) { return Err(anyhow!("no frontmatter")); } } let mut seen_backticks = 0; let mut end_index = None; while let Some((idx, c)) = chars.next() { if c == '`' { seen_backticks += 1; if seen_backticks == 3 { end_index = Some(idx - 3); break; } } else { seen_backticks = 0; } } let end_index = end_index.ok_or(anyhow!("missing frontmatter end"))?; let frontmatter = &contents[3..=end_index]; let deserialized = toml::from_str::<'de, D>(frontmatter)?; Ok((deserialized, chars.as_str())) }