use chrono::TimeDelta; use maud::{Markup, Render, html}; use serde::Deserialize; use std::pin::Pin; use crate::RequestContext; // // MARK: metadata // #[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize)] pub struct PageMetadata { pub title: String, pub author: Option, pub description: Option, pub image: Option, pub backlinks: Option, } impl Default for PageMetadata { fn default() -> Self { Self { title: "Untitled page".into(), author: None, description: None, image: None, backlinks: None, } } } impl Render for PageMetadata { fn render(&self) -> Markup { let empty = String::new(); let title = &self.title; let author = &self.author.as_ref().unwrap_or(&empty); let description = &self.description.as_ref().unwrap_or(&empty); let image = &self.image.as_ref().unwrap_or(&empty); html !( meta property="og:site_name" content=(title) {} meta name="title" content=(title) {} meta property="og:title" content=(title) {} meta property="twitter:title" content=(title) {} meta name="author" content=(author) {} meta name="description" content=(description) {} 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" {} ) } } // // MARK: page // // Some HTML pub struct Page { pub meta: PageMetadata, /// How long this page's html may be cached. /// This controls the maximum age of a page shown to the user. /// /// If `None`, this page is always rendered from scratch. pub html_ttl: Option, /// A function that generates this page's html. /// /// This should return the contents of this page's tag, /// or the contents of a wrapper element (defined in the page server struct). /// /// This closure must never return `` or ``. pub generate_html: Box< dyn Send + Sync + for<'a> Fn( &'a Page, &'a RequestContext, ) -> Pin + 'a + Send + Sync>>, >, } impl Default for Page { fn default() -> Self { Page { meta: Default::default(), html_ttl: Some(TimeDelta::seconds(60 * 24 * 30)), //css_ttl: Duration::from_secs(60 * 24 * 30), //generate_css: None, generate_html: Box::new(|_, _| Box::pin(async { html!() })), } } } impl Page { pub async fn generate_html(&self, req_info: &RequestContext) -> Markup { (self.generate_html)(self, req_info).await } }