use std::collections::HashMap;
use std::path::PathBuf;
pub const FALLBACK_WIDTH: u32 = 800;
pub const FALLBACK_HEIGHT: u32 = 600;
#[derive(Debug, Default, Clone)]
pub struct AssetSnapshot {
pub dimensions: HashMap<PathBuf, (u32, u32)>,
pub lqip: HashMap<PathBuf, String>,
pub variants: HashMap<PathBuf, VariantKindSet>,
pub dominant_color: HashMap<PathBuf, String>,
pub animated: HashMap<PathBuf, bool>,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct VariantKindSet {
pub webp: bool,
pub avif: bool,
}
impl AssetSnapshot {
pub fn new() -> Self {
Self::default()
}
pub fn dims(&self, path: &PathBuf) -> Option<(u32, u32)> {
self.dimensions.get(path).copied()
}
pub fn lqip(&self, path: &PathBuf) -> Option<&str> {
self.lqip.get(path).map(String::as_str)
}
pub fn is_animated(&self, path: &PathBuf) -> bool {
self.animated.get(path).copied().unwrap_or(false)
}
pub fn has_webp_for_source(&self, src: &PathBuf) -> bool {
let stem = path_strip_extension(src);
self.variants.get(&stem).map_or(false, |v| v.webp)
}
pub fn has_avif_for_source(&self, src: &PathBuf) -> bool {
let stem = path_strip_extension(src);
self.variants.get(&stem).map_or(false, |v| v.avif)
}
}
pub fn path_strip_extension(p: &PathBuf) -> PathBuf {
use std::path::Path;
let stem = p.file_stem().unwrap_or_default();
p.parent().unwrap_or_else(|| Path::new("")).join(stem)
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn snapshot_default_is_empty() {
let s = AssetSnapshot::new();
assert_eq!(s.dims(&PathBuf::from("x.jpg")), None);
assert_eq!(s.lqip(&PathBuf::from("x.jpg")), None);
assert!(!s.has_webp_for_source(&PathBuf::from("x.jpg")));
}
#[test]
fn snapshot_lookups() {
let mut s = AssetSnapshot::new();
s.dimensions.insert("photo.jpg".into(), (1024, 768));
s.lqip
.insert("photo.jpg".into(), "data:image/jpeg;base64,xx".into());
s.variants.insert(
"photo".into(),
VariantKindSet {
webp: true,
avif: false,
},
);
assert_eq!(s.dims(&"photo.jpg".into()), Some((1024, 768)));
assert_eq!(
s.lqip(&"photo.jpg".into()),
Some("data:image/jpeg;base64,xx")
);
assert!(s.has_webp_for_source(&"photo.jpg".into()));
assert!(!s.has_avif_for_source(&"photo.jpg".into()));
}
#[test]
fn snapshot_is_animated_lookup() {
let mut s = AssetSnapshot::new();
s.animated.insert("assets/loop.gif".into(), true);
s.animated.insert("assets/still.jpg".into(), false);
assert!(s.is_animated(&"assets/loop.gif".into()));
assert!(!s.is_animated(&"assets/still.jpg".into()));
assert!(!s.is_animated(&"assets/unknown.png".into()));
}
#[test]
fn snapshot_has_webp_for_source_strips_extension() {
let mut s = AssetSnapshot::new();
let stem = path_strip_extension(&"assets/photo.jpg".into());
s.variants.insert(
stem,
VariantKindSet {
webp: true,
avif: false,
},
);
assert!(s.has_webp_for_source(&"assets/photo.jpg".into()));
assert!(!s.has_avif_for_source(&"assets/photo.jpg".into()));
assert!(!s.has_webp_for_source(&"assets/other.jpg".into()));
}
#[test]
fn path_strip_extension_basic() {
assert_eq!(
path_strip_extension(&PathBuf::from("assets/photo.jpg")),
PathBuf::from("assets/photo")
);
assert_eq!(
path_strip_extension(&PathBuf::from("photo.webp")),
PathBuf::from("photo")
);
assert_eq!(
path_strip_extension(&PathBuf::from("assets/photo")),
PathBuf::from("assets/photo")
);
assert_eq!(
path_strip_extension(&PathBuf::from("a/b/c/photo.png")),
PathBuf::from("a/b/c/photo")
);
}
}