use anyhow::{Context, Result}; use maud::{html, Markup}; use serde::Deserialize; use toml::value::Date; use self::page::Page; mod page; #[derive(Deserialize)] struct Data { stories: Vec, } impl page::Page for Vec { fn page_title() -> String { String::from("Stories") } fn render_content(self) -> Markup { html!( @for story in self { (story.render_as_card()) } ) } fn prepare_content(&mut self) -> Result<()> { self.sort_by(|a, b| { let date1 = a.update_date.unwrap_or(a.upload_date); let date2 = b.update_date.unwrap_or(b.upload_date); date2.cmp(&date1) }); for story in self { std::fs::copy(&story.path, story.format_local_pdf_path())?; } Ok(()) } fn content_style() -> Markup { html!( """ h2 { font-size: 1.8rem; margin-bottom: 2rem; color: #2F3C57; } hr { margin: 1rem; } .story-card { text-align: left; font-size: 1.2rem; ul { display: flex; justify-content: start; list-style: none; flex-wrap: wrap; li { font-size: 1rem; padding: 0.8rem; margin: 0.8rem; background-color: #2F3C57; color: #ffd1a3; outline: 0.2rem solid #2F3C57; a { text-decoration: underline #E1CCEB; color: #E1CCEB; } } } hr { margin: 2rem 0; } padding: 3rem; margin: 3rem; background-color: #ffd1a3; border-bottom: 0.1rem dotted black; } """ ) } } #[derive(Deserialize, Debug)] enum StoryLanguage { German, English, } impl std::fmt::Display for StoryLanguage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) } } #[derive(Deserialize)] struct Story { name: String, description: String, language: StoryLanguage, upload_date: Date, update_date: Option, path: String, } impl Story { fn render_as_card(self) -> Markup { html!(div class="story-card" { card { h2 { (self.name) } p { (self.description) } hr {} ul { li { "Language: " (self.language) } li { "Uploaded on: " (self.upload_date) } @if let Some(update_date) = self.update_date { li { "Updated on: " (update_date) } } li { a href=(&self.format_pdf_path()) download { "PDF" } } } } }) } fn format_pdf_path(&self) -> String { let snake_case_name = self.name.to_lowercase().replace(" ", "_"); format!("stories/{}.pdf", snake_case_name) } fn format_local_pdf_path(&self) -> String { format!("dist/{}", self.format_pdf_path()) } } fn main() -> Result<()> { let data_file_content = std::fs::read_to_string("data.toml").with_context(|| "Failed to read data.toml")?; let data: Data = toml::from_str(&data_file_content)?; data.stories.write_page()?; Ok(()) }