use camino::Utf8Component;
use camino::{Utf8Path, Utf8PathBuf};
pub fn to_slug(path: impl AsRef<Utf8Path>) -> Utf8PathBuf {
let path = path.as_ref().with_extension("");
if let Some("index") = path.file_name() {
if let Some(parent) = path.parent() {
return parent.to_path_buf();
}
}
path.to_path_buf()
}
pub fn normalize_path(path: &Utf8Path) -> Utf8PathBuf {
let mut components = path.components().peekable();
let mut ret = if let Some(c @ Utf8Component::Prefix(..)) = components.peek().cloned() {
components.next();
Utf8PathBuf::from(c.as_str())
} else {
Utf8PathBuf::new()
};
for component in components {
match component {
Utf8Component::Prefix(..) => unreachable!(),
Utf8Component::RootDir => {
ret.push(Utf8Component::RootDir);
}
Utf8Component::CurDir => {}
Utf8Component::ParentDir => {
if ret.ends_with(Utf8Component::ParentDir) {
ret.push(Utf8Component::ParentDir);
} else {
let popped = ret.pop();
if !popped && !ret.has_root() {
ret.push(Utf8Component::ParentDir);
}
}
}
Utf8Component::Normal(c) => {
ret.push(c);
}
}
}
ret
}
pub fn normalize_prefixed(prefix: &str, path: impl AsRef<Utf8Path>) -> Utf8PathBuf {
let path = path.as_ref().strip_prefix(prefix).unwrap_or(path.as_ref());
normalize(path)
}
pub fn normalize(path: impl AsRef<Utf8Path>) -> Utf8PathBuf {
let mut buffer = path.as_ref().to_path_buf();
if let Some(file_name) = buffer.file_name() {
if file_name == "index" || file_name.starts_with("index.") {
buffer.set_extension("html");
} else {
buffer.set_extension("");
buffer.push("index.html");
}
} else {
buffer.push("index.html");
}
buffer
}
pub fn absolutize(prefix: &str, path: impl AsRef<Utf8Path>) -> Utf8PathBuf {
let path = path.as_ref().strip_prefix(prefix).unwrap_or(path.as_ref());
let path = Utf8Path::new("/").join(path);
if let Some(file_name) = path.file_name() {
if file_name == "index" || file_name.starts_with("index.") {
path.parent().unwrap().to_path_buf()
} else {
path.with_extension("")
}
} else {
path
}
}
#[derive(Debug, Clone)]
pub struct Output {
pub url: Utf8PathBuf,
pub content: String,
}
impl Output {
pub fn mapper(source: impl Into<Utf8PathBuf>) -> OutputBuilder {
OutputBuilder {
current: source.into(),
}
}
pub fn html(path: impl AsRef<Utf8Path>, content: impl Into<String>) -> Self {
Self {
url: normalize(path),
content: content.into(),
}
}
pub fn file(path: impl Into<Utf8PathBuf>, content: impl Into<String>) -> Self {
Self {
url: path.into(),
content: content.into(),
}
}
}
pub struct OutputBuilder {
current: Utf8PathBuf,
}
impl OutputBuilder {
pub fn strip_prefix(
mut self,
prefix: impl AsRef<Utf8Path>,
) -> Result<Self, crate::error::HauchiwaError> {
self.current = self
.current
.strip_prefix(prefix.as_ref())
.map(|p| p.to_path_buf())
.map_err(|_| {
anyhow::anyhow!(
"Path {} does not start with prefix {}",
self.current,
prefix.as_ref().as_str()
)
})
.map_err(|e| crate::error::HauchiwaError::Build(crate::error::BuildError::Other(e)))?;
Ok(self)
}
pub fn html(mut self) -> Self {
self.current = to_slug(&self.current).join("index").with_extension("html");
self
}
pub fn ext(mut self, extension: &str) -> Self {
self.current.set_extension(extension);
self
}
pub fn content(self, body: impl Into<String>) -> Output {
let url = if (self.current.extension() == Some("html"))
|| self.current.file_name() == Some("index")
{
normalize(&self.current)
} else {
normalize_path(&self.current)
};
Output {
url,
content: body.into(),
}
}
}
use std::fs;
use std::io;
use std::path::Path;
pub(crate) fn save_pages_to_dist(pages: &[Output]) -> io::Result<()> {
let output_dir = Path::new("dist");
fs::create_dir_all(output_dir)?;
for page in pages {
let file_path = output_dir.join(&page.url);
if let Some(parent_dir) = file_path.parent() {
fs::create_dir_all(parent_dir)?;
}
fs::write(&file_path, &page.content)?;
}
Ok(())
}