use std::borrow::Cow;
use std::collections::HashMap;
use once_cell::sync::OnceCell;
use parking_lot::RwLock;
pub struct EmbeddedAsset {
pub data: Cow<'static, [u8]>,
pub content_type: String,
pub etag: Option<String>,
pub last_modified: Option<u64>,
}
pub type EmbeddedAssetFetcher = fn(&str) -> Option<EmbeddedAsset>;
static REGISTRY: OnceCell<RwLock<HashMap<String, EmbeddedAssetFetcher>>> = OnceCell::new();
fn registry() -> &'static RwLock<HashMap<String, EmbeddedAssetFetcher>> {
REGISTRY.get_or_init(|| RwLock::new(HashMap::new()))
}
pub fn register(prefix: impl Into<String>, fetcher: EmbeddedAssetFetcher) {
let mut map = registry().write();
map.insert(prefix.into(), fetcher);
}
pub fn lookup(prefix: &str) -> Option<EmbeddedAssetFetcher> {
registry().read().get(prefix).copied()
}
pub fn guess_mime(path: &str) -> String {
mime_guess::from_path(path)
.first_or_octet_stream()
.essence_str()
.to_string()
}
#[cfg(feature = "embed-assets")]
pub fn fetcher_from<E: rust_embed::RustEmbed + ?Sized>(path: &str) -> Option<EmbeddedAsset> {
let file = E::get(path)?;
let etag = file
.metadata
.sha256_hash()
.iter()
.map(|b| format!("{b:02x}"))
.collect::<String>();
Some(EmbeddedAsset {
content_type: guess_mime(path),
etag: Some(etag),
last_modified: file.metadata.last_modified(),
data: file.data,
})
}
#[cfg(feature = "embed-assets")]
#[macro_export]
macro_rules! embed_static {
($struct_name:ident, $prefix:expr, $folder:expr) => {
#[derive($crate::rust_embed::RustEmbed)]
#[folder = $folder]
pub struct $struct_name;
pub fn fetcher(path: &str) -> ::core::option::Option<$crate::embedded::EmbeddedAsset> {
$crate::embedded::fetcher_from::<$struct_name>(path)
}
pub fn register() {
$crate::embedded::register($prefix, fetcher);
}
};
}