diff --git a/crates/macro/macro-assets/src/lib.rs b/crates/macro/macro-assets/src/lib.rs index bb57a6f..4445f3a 100644 --- a/crates/macro/macro-assets/src/lib.rs +++ b/crates/macro/macro-assets/src/lib.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; use quote::quote; use syn::{ - Ident, LitStr, Result, Token, braced, + Expr, Ident, LitStr, Result, Token, braced, parse::{Parse, ParseStream}, parse_macro_input, }; @@ -92,7 +92,7 @@ pub fn assets(input: TokenStream) -> TokenStream { // Generate documentation showing the original asset definition let doc = format!( - "This is an `asset!`\n```notrust\n{} {{\n\tsource: \"{}\",\n\ttarget: \"{}\"\n}}\n```", + "This is an `asset!`\n```notrust\n{} {{\n\tsource: \"{:?}\",\n\ttarget: \"{}\"\n}}\n```", name, source, target ); @@ -106,7 +106,7 @@ pub fn assets(input: TokenStream) -> TokenStream { const URL_PREFIX: &'static str = #prefix; const URL_POSTFIX: &'static str = #target; const URL: &'static str = concat!(#prefix, #target); - const BYTES: &'static [u8] = include_bytes!(#source); + const BYTES: &'static [u8] = #source; } } }); @@ -115,9 +115,20 @@ pub fn assets(input: TokenStream) -> TokenStream { let router_fn = if let Some(router_name) = &input.router { let route_definitions = input.assets.iter().map(|asset| { let name = &asset.name; + + let headers = asset + .headers + .as_ref() + .map(|x| quote! { #x }) + .unwrap_or(quote! { [] }); + quote! { .route(#name::URL_POSTFIX, ::axum::routing::get(|| async { - (::axum::http::StatusCode::OK, #name::BYTES) + ( + ::axum::http::StatusCode::OK, + #headers, + #name::BYTES + ) })) } }); @@ -159,8 +170,9 @@ struct AssetsInput { /// Represents a single asset definition within the macro struct AssetDefinition { name: Ident, - source: String, + source: Expr, target: String, + headers: Option, } impl Parse for AssetsInput { @@ -214,26 +226,60 @@ impl Parse for AssetDefinition { let content; braced!(content in input); - // Parse "source:" - let _source_ident: Ident = content.parse()?; - let _colon1: Token![:] = content.parse()?; - let source_lit: LitStr = content.parse()?; - let source = source_lit.value(); - let _comma1: Token![,] = content.parse()?; + // Parse fields in any order + let mut source: Option = None; + let mut target: Option = None; + let mut headers: Option = None; - // Parse "target:" - let _target_ident: Ident = content.parse()?; - let _colon2: Token![:] = content.parse()?; - let target_lit: LitStr = content.parse()?; - let target = target_lit.value(); + while !content.is_empty() { + // Parse field name + let field_name: Ident = content.parse()?; + let _colon: Token![:] = content.parse()?; - // Optional trailing comma - let _ = content.parse::(); + // Parse field value based on name + match field_name.to_string().as_str() { + "source" => { + if source.is_some() { + return Err(syn::Error::new(field_name.span(), "duplicate 'source' field")); + } + source = Some(content.parse()?); + } + "target" => { + if target.is_some() { + return Err(syn::Error::new(field_name.span(), "duplicate 'target' field")); + } + let target_lit: LitStr = content.parse()?; + target = Some(target_lit.value()); + } + "headers" => { + if headers.is_some() { + return Err(syn::Error::new(field_name.span(), "duplicate 'headers' field")); + } + headers = Some(content.parse()?); + } + _ => { + return Err(syn::Error::new( + field_name.span(), + format!("unknown field '{}', expected 'source', 'target', or 'headers'", field_name) + )); + } + } + + // Parse comma if not at end + if !content.is_empty() { + content.parse::()?; + } + } + + // Validate required fields + let source = source.ok_or_else(|| syn::Error::new(name.span(), "missing required field 'source'"))?; + let target = target.ok_or_else(|| syn::Error::new(name.span(), "missing required field 'target'"))?; Ok(AssetDefinition { name, source, target, + headers, }) } } diff --git a/crates/service/service-webpage/src/components/base.rs b/crates/service/service-webpage/src/components/base.rs index 93790e7..a91b63e 100644 --- a/crates/service/service-webpage/src/components/base.rs +++ b/crates/service/service-webpage/src/components/base.rs @@ -1,7 +1,7 @@ -use macro_sass::sass; +use assetserver::Asset; use maud::{DOCTYPE, Markup, PreEscaped, Render, html}; -use crate::components::misc::FarLink; +use crate::{components::misc::FarLink, routes::assets::Styles_Main}; pub struct PageMetadata { pub title: String, @@ -30,15 +30,12 @@ impl Render for PageMetadata { meta property="og:description" content=(description) {} meta property="twitter:description" content=(description) {} - meta content=(image) property="og:image" {} link rel="shortcut icon" href=(image) type="image/x-icon" {} ) } } -const CSS: &str = sass!("css/main.scss"); - pub struct BasePage(pub PageMetadata, pub T); impl Render for BasePage { @@ -54,9 +51,11 @@ impl Render for BasePage { meta content="text/html; charset=UTF-8" http-equiv="content-type" {} meta property="og:type" content="website" {} + link rel="stylesheet" href=(Styles_Main::URL) {} + + (meta) title { (PreEscaped(meta.title.clone())) } - style { (PreEscaped(CSS)) } } body { diff --git a/crates/service/service-webpage/src/routes/assets.rs b/crates/service/service-webpage/src/routes/assets.rs index 230051e..064a082 100644 --- a/crates/service/service-webpage/src/routes/assets.rs +++ b/crates/service/service-webpage/src/routes/assets.rs @@ -1,27 +1,50 @@ use assetserver::Asset; +use axum::http::header; use macro_assets::assets; +use macro_sass::sass; assets! { prefix: "/assets" router: asset_router() + // + // MARK: styles + // + + Styles_Main { + source: sass!("css/main.scss").as_bytes(), + target: "/css/main.css", + headers: [ + (header::CONTENT_TYPE, "text/css") + ] + } + // // MARK: images // Image_Cover { - source: "../../assets/images/cover-small.jpg", - target: "/img/face.jpg" + source: include_bytes!("../../assets/images/cover-small.jpg"), + target: "/img/face.jpg", + headers: [ + (header::CONTENT_TYPE, "image/jpg") + ] } Image_Betalupi { - source: "../../assets/images/betalupi-map.png", - target: "/img/betalupi.png" + source: include_bytes!("../../assets/images/betalupi-map.png"), + target: "/img/betalupi.png", + headers: [ + (header::CONTENT_TYPE, "image/png") + ] } Image_Icon { - source: "../../assets/images/icon.png", - target: "/img/icon.png" + source: include_bytes!("../../assets/images/icon.png"), + target: "/img/icon.png", + headers: [ + (header::CONTENT_TYPE, "image/png") + ] } // @@ -29,33 +52,51 @@ assets! { // FiraCode_Bold_woff2 { - source: "../../assets/fonts/fira/FiraCode-Bold.woff2", - target: "/fonts/FiraCode-Bold.woff2" + source: include_bytes!("../../assets/fonts/fira/FiraCode-Bold.woff2"), + target: "/fonts/FiraCode-Bold.woff2", + headers: [ + (header::CONTENT_TYPE, "application/font-woff2") + ] } FiraCode_Light_woff2 { - source: "../../assets/fonts/fira/FiraCode-Light.woff2", - target: "/fonts/FiraCode-Light.woff2" + source: include_bytes!("../../assets/fonts/fira/FiraCode-Light.woff2"), + target: "/fonts/FiraCode-Light.woff2", + headers: [ + (header::CONTENT_TYPE, "application/font-woff2") + ] } FiraCode_Medium_woff2 { - source: "../../assets/fonts/fira/FiraCode-Medium.woff2", - target: "/fonts/FiraCode-Medium.woff2" + source: include_bytes!("../../assets/fonts/fira/FiraCode-Medium.woff2"), + target: "/fonts/FiraCode-Medium.woff2", + headers: [ + (header::CONTENT_TYPE, "application/font-woff2") + ] } FiraCode_Regular_woff2 { - source: "../../assets/fonts/fira/FiraCode-Regular.woff2", - target: "/fonts/FiraCode-Regular.woff2" + source: include_bytes!("../../assets/fonts/fira/FiraCode-Regular.woff2"), + target: "/fonts/FiraCode-Regular.woff2", + headers: [ + (header::CONTENT_TYPE, "application/font-woff2") + ] } FiraCode_SemiBold_woff2 { - source: "../../assets/fonts/fira/FiraCode-SemiBold.woff2", - target: "/fonts/FiraCode-SemiBold.woff2" + source: include_bytes!("../../assets/fonts/fira/FiraCode-SemiBold.woff2"), + target: "/fonts/FiraCode-SemiBold.woff2", + headers: [ + (header::CONTENT_TYPE, "application/font-woff2") + ] } FiraCode_VF_woff2 { - source: "../../assets/fonts/fira/FiraCode-VF.woff2", - target: "/fonts/FiraCode-VF.woff2" + source: include_bytes!("../../assets/fonts/fira/FiraCode-VF.woff2"), + target: "/fonts/FiraCode-VF.woff2", + headers: [ + (header::CONTENT_TYPE, "application/font-woff2") + ] } // @@ -63,32 +104,50 @@ assets! { // Fa_Brands_woff2 { - source: "../../assets/fonts/fa/fa-brands-400.woff2", - target: "/fonts/fa/fa-brands-400.woff2" + source: include_bytes!("../../assets/fonts/fa/fa-brands-400.woff2"), + target: "/fonts/fa/fa-brands-400.woff2", + headers: [ + (header::CONTENT_TYPE, "application/font-woff2") + ] } Fa_Regular_woff2 { - source: "../../assets/fonts/fa/fa-regular-400.woff2", - target: "/fonts/fa/fa-regular-400.woff2" + source: include_bytes!("../../assets/fonts/fa/fa-regular-400.woff2"), + target: "/fonts/fa/fa-regular-400.woff2", + headers: [ + (header::CONTENT_TYPE, "application/font-woff2") + ] } Fa_Solid_woff2 { - source: "../../assets/fonts/fa/fa-solid-900.woff2", - target: "/fonts/fa/fa-solid-900.woff2" + source: include_bytes!("../../assets/fonts/fa/fa-solid-900.woff2"), + target: "/fonts/fa/fa-solid-900.woff2", + headers: [ + (header::CONTENT_TYPE, "application/font-woff2") + ] } Fa_Brands_ttf { - source: "../../assets/fonts/fa/fa-brands-400.ttf", - target: "/fonts/fa/fa-brands-400.ttf" + source: include_bytes!("../../assets/fonts/fa/fa-brands-400.ttf"), + target: "/fonts/fa/fa-brands-400.ttf", + headers: [ + (header::CONTENT_TYPE, "application/font-ttf") + ] } Fa_Regular_ttf { - source: "../../assets/fonts/fa/fa-regular-400.ttf", - target: "/fonts/fa/fa-regular-400.ttf" + source: include_bytes!("../../assets/fonts/fa/fa-regular-400.ttf"), + target: "/fonts/fa/fa-regular-400.ttf", + headers: [ + (header::CONTENT_TYPE, "application/font-ttf") + ] } Fa_Solid_ttf { - source: "../../assets/fonts/fa/fa-solid-900.ttf", - target: "/fonts/fa/fa-solid-900.ttf" + source: include_bytes!("../../assets/fonts/fa/fa-solid-900.ttf"), + target: "/fonts/fa/fa-solid-900.ttf", + headers: [ + (header::CONTENT_TYPE, "application/font-ttf") + ] } } diff --git a/crates/service/service-webpage/src/routes/mod.rs b/crates/service/service-webpage/src/routes/mod.rs index b128a1d..a0d3be6 100644 --- a/crates/service/service-webpage/src/routes/mod.rs +++ b/crates/service/service-webpage/src/routes/mod.rs @@ -3,7 +3,7 @@ use axum::routing::get; use tracing::info; use utoipa::OpenApi; -mod assets; +pub mod assets; mod betalupi; mod index; mod links;