mod args;
mod walk;
use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;
use crate::assets::{args::AssetArgs, walk::Walked};
pub(crate) fn expand(item: TokenStream) -> TokenStream {
let args = parse_macro_input!(item as AssetArgs);
match expand_inner(&args) {
Ok(tokens) => tokens.into(),
Err(error) => error.to_compile_error().into(),
}
}
pub(crate) fn expand_inner(args: &AssetArgs) -> syn::Result<proc_macro2::TokenStream> {
let walked = walk::walk(args)?;
let name = &args.name;
let visibility = &args.visibility;
let docs = &args.docs;
let entries = walked.files.iter().map(|file| {
let path = &file.path;
let absolute = &file.absolute;
let etag = &file.etag;
if file.encodings.is_empty() {
return quote! {
::kynos::router::assets::Asset::embedded(
#path,
::core::include_bytes!(#absolute),
#etag,
)
};
}
let encodings = file.encodings.iter().map(|encoded| {
let coding = encoded.coding;
let absolute = &encoded.absolute;
let etag = &encoded.etag;
quote! {
::kynos::router::assets::Encoded::stored(
#coding,
::core::include_bytes!(#absolute),
#etag,
)
}
});
quote! {
::kynos::router::assets::Asset::embedded_with_codings(
#path,
::core::include_bytes!(#absolute),
#etag,
&[#(#encodings),*],
)
}
});
let total = walked.total_bytes;
let count = walked.files.len();
let guard = size_guard(args, &walked);
Ok(quote! {
#(#docs)*
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#visibility struct #name;
impl #name {
pub const ASSETS: &'static [::kynos::router::assets::Asset] = &[#(#entries),*];
pub const TOTAL_BYTES: usize = #total;
pub const COUNT: usize = #count;
#[must_use]
#visibility fn assets() -> ::kynos::router::assets::AssetSet {
::kynos::router::assets::AssetSet::embedded(Self::ASSETS)
}
}
#guard
})
}
fn size_guard(args: &AssetArgs, walked: &Walked) -> Option<proc_macro2::TokenStream> {
let threshold = args.warn_over?;
if walked.total_bytes <= threshold {
return None;
}
let total = walked.total_bytes;
let directory = args.dir.value();
let suggestion = suggest(walked.total_bytes);
let note = format!(
"`assets!` embedded {total} bytes from `{directory}` into this binary, past the \
{threshold}-byte threshold. Binaries this size are slow to link and costly to ship; \
serve them from a CDN, or from disk with `assets-fs`. Raise the threshold with \
`warn_over = \"{suggestion}\"`, or turn the check off with `warn_over = \"none\"`."
);
let span = args.dir.span();
let marker = syn::Ident::new("this_embedded_asset_set_is_large", span);
Some(quote::quote_spanned! { span =>
const _: () = {
#[deprecated(note = #note)]
#[allow(non_upper_case_globals)]
const #marker: () = ();
let _ = #marker;
};
})
}
fn suggest(total: usize) -> String {
let mebibytes = total.div_ceil(1024 * 1024).max(1);
format!("{}MiB", mebibytes.next_power_of_two())
}
#[cfg(test)]
mod tests;