use crate::*;
pub use prest_embed_macro::Embed;
pub use prest_embed_utils::*;
use std::borrow::Cow;
pub trait EmbeddedStruct {
fn iter() -> __Filenames;
fn get(file_path: &str) -> Option<EmbeddedFile>;
fn get_content(file_path: &str) -> Option<String> {
if let Some(file) = Self::get(file_path) {
Some(match std::str::from_utf8(&file.data) {
Ok(content) => content.to_owned(),
Err(e) => format!("Invalid UTF-8 content: {e}"),
})
} else {
None
}
}
}
pub trait EmbedRoutes {
fn embed<T: EmbeddedStruct>(self, _: T) -> Self;
}
impl EmbedRoutes for Router {
fn embed<T: EmbeddedStruct>(mut self, _: T) -> Self {
for path in T::iter() {
self = self.route(
&format!("/{path}"),
get(|headers: HeaderMap| async move { file_handler::<T>(&path, headers) }),
)
}
self
}
}
fn file_handler<T: EmbeddedStruct + ?Sized>(path: &str, headers: HeaderMap) -> Result<Response> {
let Some(asset) = T::get(&path) else {
return Ok(StatusCode::NOT_FOUND.into_response());
};
let asset_etag = hex::encode(asset.metadata.sha256_hash());
if let Some(request_etag) = headers.get(header::IF_NONE_MATCH) {
if request_etag.as_bytes() == asset_etag.as_bytes() {
return Ok(StatusCode::NOT_MODIFIED.into_response());
}
}
#[allow(unused_mut)]
let mut response = Response::builder();
#[cfg(not(debug_assertions))]
if path == "sw.js" || path == "sw.wasm" {
response = response.header(
header::CACHE_CONTROL,
"max-age=60, stale-while-revalidate=3600, stale-if-error=604800",
);
}
Ok(response
.header(header::ETAG, asset_etag)
.header(header::CONTENT_TYPE, asset.metadata.mimetype())
.body(Body::from(asset.data))?)
}
#[macro_export]
macro_rules! embed_build_output_as {
($struct_name:ident) => {
#[derive(Embed)]
#[folder = "$OUT_DIR"]
pub struct $struct_name;
};
}
/// One-liner for structs with derived Embed
#[macro_export]
macro_rules! embed_as {
($struct_name:ident from $path:literal $(only $($inc:literal),+)? $(except $($exc:literal),+)?) => {
#[derive(Embed)]
#[folder = $path]
$( $( #[include = $inc] )+ )?
$( $( #[exclude = $exc] )+ )?
pub struct $struct_name;
};
}
#[doc(hidden)]
pub enum __Filenames {
#[cfg(any(not(debug_assertions), target_arch = "wasm32"))]
Embedded(std::slice::Iter<'static, &'static str>),
#[cfg(all(debug_assertions, not(target_arch = "wasm32")))]
Dynamic(Box<dyn Iterator<Item = Cow<'static, str>>>),
}
impl Iterator for __Filenames {
type Item = Cow<'static, str>;
fn next(&mut self) -> Option<Self::Item> {
match self {
#[cfg(any(not(debug_assertions), target_arch = "wasm32"))]
__Filenames::Embedded(names) => names.next().map(|x| Cow::from(*x)),
#[cfg(all(debug_assertions, not(target_arch = "wasm32")))]
__Filenames::Dynamic(boxed) => boxed.next(),
}
}
}