|
| 1 | +use crate::{AppState, Highlighter, Page}; |
| 2 | +use askama::Template; |
| 3 | +use axum::extract::State; |
| 4 | +use std::num::{NonZero, NonZeroU32}; |
| 5 | +use std::sync::OnceLock; |
| 6 | + |
| 7 | +/// GET handler for the index page. |
| 8 | +pub async fn index( |
| 9 | + State(state): State<AppState>, |
| 10 | + State(page): State<Page>, |
| 11 | + State(highlighter): State<Highlighter>, |
| 12 | +) -> Index { |
| 13 | + Index { |
| 14 | + page, |
| 15 | + max_expiration: state.max_expiration, |
| 16 | + highlighter, |
| 17 | + } |
| 18 | +} |
| 19 | + |
| 20 | +/// Index page displaying a form for paste insertion and a selection box for languages. |
| 21 | +#[derive(Template)] |
| 22 | +#[template(path = "index.html")] |
| 23 | +pub struct Index { |
| 24 | + page: Page, |
| 25 | + max_expiration: Option<NonZeroU32>, |
| 26 | + highlighter: Highlighter, |
| 27 | +} |
| 28 | + |
| 29 | +#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
| 30 | +enum Expiration { |
| 31 | + None, |
| 32 | + Burn, |
| 33 | + Time(NonZeroU32), |
| 34 | +} |
| 35 | + |
| 36 | +impl std::fmt::Display for Expiration { |
| 37 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 38 | + match self { |
| 39 | + Expiration::None => write!(f, ""), |
| 40 | + Expiration::Burn => write!(f, "burn"), |
| 41 | + Expiration::Time(t) => write!(f, "{t}"), |
| 42 | + } |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +#[allow(clippy::unwrap_used)] |
| 47 | +const EXPIRATION_OPTIONS: [(&str, Expiration); 8] = [ |
| 48 | + ("never", Expiration::None), |
| 49 | + ("10 minutes", Expiration::Time(NonZero::new(600).unwrap())), |
| 50 | + ("1 hour", Expiration::Time(NonZero::new(3600).unwrap())), |
| 51 | + ("1 day", Expiration::Time(NonZero::new(86400).unwrap())), |
| 52 | + ("1 week", Expiration::Time(NonZero::new(604_800).unwrap())), |
| 53 | + ( |
| 54 | + "1 month", |
| 55 | + Expiration::Time(NonZero::new(2_592_000).unwrap()), |
| 56 | + ), |
| 57 | + ( |
| 58 | + "1 year", |
| 59 | + Expiration::Time(NonZero::new(31_536_000).unwrap()), |
| 60 | + ), |
| 61 | + ("🔥 after reading", Expiration::Burn), |
| 62 | +]; |
| 63 | + |
| 64 | +impl Index { |
| 65 | + fn expiry_options(&self) -> &str { |
| 66 | + static EXPIRATION_OPTIONS_HTML: OnceLock<String> = OnceLock::new(); |
| 67 | + |
| 68 | + EXPIRATION_OPTIONS_HTML.get_or_init(|| { |
| 69 | + |
| 70 | + let mut option_set = String::new(); |
| 71 | + let mut wrote_first = false; |
| 72 | + |
| 73 | + option_set.push('\n'); |
| 74 | + |
| 75 | + for (opt_name, opt_val) in EXPIRATION_OPTIONS { |
| 76 | + if self.max_expiration.is_none() |
| 77 | + || opt_val == Expiration::Burn |
| 78 | + || matches!((self.max_expiration, opt_val), (Some(exp), Expiration::Time(time)) if time <= exp) |
| 79 | + { |
| 80 | + option_set.push_str("<option"); |
| 81 | + if !wrote_first { |
| 82 | + option_set.push_str(" selected"); |
| 83 | + wrote_first = true; |
| 84 | + } |
| 85 | + option_set.push_str(" value=\""); |
| 86 | + option_set.push_str(opt_val.to_string().as_ref()); |
| 87 | + option_set.push_str("\">"); |
| 88 | + option_set.push_str(opt_name); |
| 89 | + option_set.push_str("</option>\n"); |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + option_set |
| 94 | + }) |
| 95 | + } |
| 96 | +} |
0 commit comments