hauchiwa 0.0.1

Static site generator library
Documentation
use std::{collections::HashMap, fs};

use camino::{Utf8Path, Utf8PathBuf};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

use crate::tree::{AssetKind, Output, OutputKind};

pub(crate) fn hash_assets(
	cache: &Utf8Path,
	items: &[&Output],
) -> HashMap<Utf8PathBuf, Utf8PathBuf> {
	fs::create_dir_all(cache).unwrap();

	items
		.par_iter()
		.filter_map(|item| match item.kind {
			OutputKind::Asset(ref asset) => match asset.kind {
				AssetKind::Image => {
					let buffer = std::fs::read(&asset.meta.path).expect("Couldn't read file");
					let format = image::guess_format(&buffer).expect("Couldn't read format");

					if matches!(format, image::ImageFormat::Gif) {
						return None;
					}

					let path = item.path.to_owned();
					let hash = hash_image(cache, &buffer, &asset.meta.path);
					Some((path, hash))
				}
				_ => None,
			},
			_ => None,
		})
		.collect()
}

fn optimize_image(buffer: &[u8], file: &Utf8Path, path: &Utf8Path) -> Vec<u8> {
	println!("Hashing image {} -> {}", file, path);
	let img = image::load_from_memory(buffer).expect("Couldn't load image");
	let dim = (img.width(), img.height());

	let mut out = Vec::new();
	let encoder = image::codecs::webp::WebPEncoder::new_lossless(&mut out);

	encoder
		.encode(&img.to_rgba8(), dim.0, dim.1, image::ColorType::Rgba8)
		.expect("Encoding error");

	out
}

fn hash_image(cache: &Utf8Path, buffer: &[u8], file: &Utf8Path) -> Utf8PathBuf {
	let hash = sha256::digest(buffer);
	let path = cache.join(&hash).with_extension("webp");

	if !path.exists() {
		let img = optimize_image(buffer, file, &path);
		fs::write(path, img).expect("Couldn't output optimized image");
	}

	Utf8Path::new("/")
		.join("hash")
		.join(hash)
		.with_extension("webp")
}