use anyhow::{Context, Result}; use maud::{html, Markup, DOCTYPE}; use serde::Deserialize; #[derive(Deserialize)] struct Data { stories: Vec, } impl Data { fn render_stories(self) -> Markup { html!( @for story in self.stories { (story.render_as_card()) } ) } } #[derive(Deserialize)] struct Story { name: String, description: String, } impl Story { fn render_as_card(self) -> Markup { html!(card { span { (self.name) } span { (self.description) } }) } } fn render_head(page_title: &str) -> Markup { html!( head { (DOCTYPE) meta charset="utf-8"; meta name="viewport" content="width=device-width,initial-scale=1"; title { (page_title) } } ) } fn render_navigation() -> Markup { html!( div class="navbar" { header { h1 { "V2 Tales"; } } nav { a href="" { "HOME"; } a href="" { "STORIES"; } } } ) } fn style() -> Markup { html!( style { """ * { padding: 0; margin: 0; } h1 { padding: 0.3rem; letter-spacing: 0.8rem; font-size: 1.5rem; color: #0d234adb; display: flex; justify-content: center; align-items: center; gap: 2rem; } h2 { font-size: 2rem; margin-bottom: 2rem; color: #0d234adb; } body { margin: 2rem 1rem; background-color: #ffc994; text-align: center; } hr { margin: 1rem; } img { max-width: 3rem; max-height: 3rem; } .navbar { display: flex; justify-content: space-between; align-items: center; padding: 0 3rem; } nav { display: flex; justify-content: space-around; width: 50%; a { text-decoration: none; color: #0d234adb; &:hover { text-decoration: underline; } } } """ } ) } 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)?; let stories_html = html!((render_head("Stories - V2 Tales"))(style())( render_navigation() )(data.render_stories())); std::fs::write("dist/stories.html", stories_html.into_string()) .with_context(|| "Failed to write dist/stories.html")?; Ok(()) }