Blog attempt 5
at trunk 144 lines 4.0 kB view raw
1#![allow(clippy::needless_pass_by_value)] 2 3use crate::template::{SpeechDetails, render_speech, speech}; 4use crate::{ 5 error::{AppError, Result}, 6 template::{SpeechCharacter, SpeechEmotion}, 7}; 8use autumnus::{formatter::Formatter as _, languages::Language}; 9use makup::Rewriter; 10use maud::{Markup, PreEscaped, html}; 11use std::collections::HashMap; 12 13fn speech_box(contents: &str, attrs: HashMap<&str, &str>) -> String { 14 let char = attrs.get("character").unwrap_or(&"deer"); 15 let emotion = attrs.get("emotion").unwrap_or(&"neutral"); 16 17 speech( 18 &match *char { 19 "you" => SpeechCharacter::You, 20 _ => SpeechCharacter::Deer, 21 }, 22 &match *emotion { 23 "worried" => SpeechEmotion::Worried, 24 "shocked" => SpeechEmotion::Shocked, 25 "happy" => SpeechEmotion::Happy, 26 _ => SpeechEmotion::Neutral, 27 }, 28 &html! { (PreEscaped(contents)) }, 29 ) 30 .into_string() 31} 32 33fn speech_box_nocss(contents: &str, attrs: HashMap<&str, &str>) -> String { 34 let char = attrs.get("character").unwrap_or(&"deer"); 35 let emotion = attrs.get("emotion").unwrap_or(&"neutral"); 36 37 let SpeechDetails { class: _, alt, src } = render_speech( 38 &match *char { 39 "you" => SpeechCharacter::You, 40 _ => SpeechCharacter::Deer, 41 }, 42 &match *emotion { 43 "worried" => SpeechEmotion::Worried, 44 "shocked" => SpeechEmotion::Shocked, 45 "happy" => SpeechEmotion::Happy, 46 _ => SpeechEmotion::Neutral, 47 }, 48 ); 49 50 html! { 51 table { 52 tbody { 53 td { 54 img width="120" height="120" src=(src) alt=(alt); 55 } 56 td { 57 span { (PreEscaped(contents))} 58 } 59 } 60 } 61 } 62 .into_string() 63} 64 65#[expect( 66 clippy::expect_used, 67 clippy::unwrap_used, 68 reason = "Function is not allowed to error, annoyingly." 69)] 70pub(crate) fn maud_code_block(text: &str, lang: &str) -> Markup { 71 let formatter = autumnus::HtmlInlineBuilder::new() 72 .lang(Language::guess(lang, text)) 73 .source(text) 74 .theme(Some( 75 autumnus::themes::get("catppuccin_mocha").expect("Built in!"), 76 )) 77 .build() 78 .expect("i hope this doesnt crash!"); 79 80 let mut output = Vec::new(); 81 formatter.format(&mut output).unwrap(); 82 83 let output = String::from_utf8(output).unwrap(); 84 html! { 85 .code-block { 86 .code-language { (lang) } 87 (PreEscaped(output)) 88 } 89 } 90} 91 92fn code_block(contents: &str, attrs: HashMap<&str, &str>) -> String { 93 let lang = attrs 94 .get("lang") 95 .or_else(|| attrs.get("language")) 96 .unwrap_or(&"plain"); 97 98 maud_code_block(contents, lang).into_string() 99} 100 101fn code_block_nocss(contents: &str, attrs: HashMap<&str, &str>) -> String { 102 let lang = attrs 103 .get("lang") 104 .or_else(|| attrs.get("language")) 105 .unwrap_or(&"plain"); 106 107 html! { 108 figure { 109 pre { 110 code { 111 (contents) 112 } 113 } 114 figcaption { 115 "Code block in " (lang) " language" 116 } 117 } 118 } 119 .into_string() 120} 121 122pub fn render(markup: &str) -> Result<String> { 123 let mut rewriter = Rewriter::default(); 124 rewriter.add_component("speech-box", speech_box); 125 rewriter.add_component("code-block", code_block); 126 127 let contents = rewriter 128 .rewrite(markup) 129 .map_err(|e| AppError::internal_server_error(e.to_string()))?; 130 131 Ok(contents) 132} 133 134pub fn no_css(markup: &str) -> Result<String> { 135 let mut rewriter = Rewriter::default(); 136 rewriter.add_component("speech-box", speech_box_nocss); 137 rewriter.add_component("code-block", code_block_nocss); 138 139 let contents = rewriter 140 .rewrite(markup) 141 .map_err(|e| AppError::internal_server_error(e.to_string()))?; 142 143 Ok(contents) 144}