Current section

Files

Jump to
rbt_weave crates weave-content src html thumbnail.rs
Raw

crates/weave-content/src/html/thumbnail.rs

//! Thumbnail URL rewriting and OG image helpers.
use std::fmt::Write as _;
use sha2::{Digest, Sha256};
use super::HtmlConfig;
use super::escape::escape_attr;
/// Length of the hex-encoded SHA-256 prefix used for thumbnail keys.
pub(crate) const THUMB_KEY_HEX_LEN: usize = 32;
/// Rewrite a thumbnail source URL to a hosted URL.
///
/// When `config.thumbnail_base_url` is set, computes the deterministic
/// key `thumbnails/{sha256_hex[0..32]}.webp` (matching `weave-image`)
/// and returns `{base_url}/thumbnails/{hash}.webp`.
///
/// When not set, returns the original URL unchanged.
pub(crate) fn rewrite_thumbnail_url(source_url: &str, config: &HtmlConfig) -> String {
match &config.thumbnail_base_url {
Some(base) => {
// If already rewritten (e.g. by loom-seed thumbnail processor), return as-is
if source_url.starts_with(base.as_str()) {
return source_url.to_string();
}
let key = thumbnail_key(source_url);
format!("{base}/{key}")
}
None => source_url.to_string(),
}
}
/// Compute the thumbnail object key from a source URL.
///
/// Returns `thumbnails/{sha256_hex[0..32]}.webp`, matching the algorithm
/// in `weave-image::thumbnail_key`.
pub(crate) fn thumbnail_key(source_url: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(source_url.as_bytes());
let hash = hasher.finalize();
let hex = hex_encode(&hash);
format!("thumbnails/{}.webp", &hex[..THUMB_KEY_HEX_LEN])
}
/// Encode bytes as lowercase hex string.
fn hex_encode(bytes: &[u8]) -> String {
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
let _ = write!(s, "{b:02x}");
}
s
}
/// Build `data-og-image` attribute string if a URL is available.
pub(crate) fn og_image_attr(url: Option<&str>, config: &HtmlConfig) -> String {
match url {
Some(u) if !u.is_empty() => {
let rewritten = rewrite_thumbnail_url(u, config);
format!(" data-og-image=\"{}\"", escape_attr(&rewritten))
}
_ => String::new(),
}
}