use chrono::{DateTime, Locale, Utc}; use lazy_static::lazy_static; use regex::{Captures, Regex}; use std::{collections::HashSet, fmt::Display}; use crate::markup::SPOILER_REGEX; lazy_static! { static ref MARKUP_QUOTE_REGEX: Regex = Regex::new(r#">>(\d+)<\/a>"#).unwrap(); } pub fn czech_humantime(time: &DateTime) -> askama::Result { let duration = (Utc::now() - *time).abs(); let seconds = duration.num_seconds(); let minutes = duration.num_minutes(); let hours = duration.num_hours(); let days = duration.num_days(); let weeks = duration.num_weeks(); let months = duration.num_days() / 30; let years = duration.num_days() / 365; let mut time = "Teď".into(); if seconds > 0 { time = format!( "{} {}", seconds, czech_plural("sekunda|sekundy|sekund", seconds)? ); } if minutes > 0 { time = format!( "{} {}", minutes, czech_plural("minuta|minuty|minut", minutes)? ); } if hours > 0 { time = format!("{} {}", hours, czech_plural("hodina|hodiny|hodin", hours)?); } if days > 0 { time = format!("{} {}", days, czech_plural("den|dny|dnů", days)?); } if weeks > 0 { time = format!("{} {}", weeks, czech_plural("týden|týdny|týdnů", weeks)?); } if months > 0 { time = format!( "{} {}", months, czech_plural("měsíc|měsíce|měsíců", months)? ); } if years > 0 { time = format!("{} {}", years, czech_plural("rok|roky|let", years)?); } Ok(time) } pub fn czech_datetime(time: &DateTime) -> askama::Result { let time = time .format_localized("%d.%m.%Y (%a) %H:%M:%S UTC", Locale::cs_CZ) .to_string(); Ok(time) } pub fn czech_plural(plurals: &str, count: impl Display) -> askama::Result { let plurals = plurals.split('|').collect::>(); let count = count.to_string().parse::().unwrap(); let one = plurals[0]; let few = plurals[1]; let other = plurals[2]; if count == 1 { Ok(one.into()) } else if count < 5 { Ok(few.into()) } else { Ok(other.into()) } } pub fn inline_post(input: impl Display) -> askama::Result { let input = input.to_string(); if input.is_empty() { return Ok("(bez obsahu)".into()); } let collapsed = input.split_whitespace().collect::>().join(" "); let spoilered = SPOILER_REGEX .replace_all(&collapsed, "(spoiler)") .to_string(); let truncated = askama::filters::truncate(spoilered, 64)?; Ok(truncated) } pub fn get_page(input: &usize, page_size: &i64) -> askama::Result { let page = crate::paginate(*page_size, *input as i64); Ok(page) } pub fn add_yous( input: impl Display, board: &String, yous: &HashSet, ) -> askama::Result { let input = input.to_string(); let output = MARKUP_QUOTE_REGEX.replace_all(&input, |captures: &Captures| { let quote = &captures[0]; let id = &captures[1]; format!( "{}{}", quote, if yous.contains(&format!("{board}/{id}")) { " (Ty)" } else { "" } ) }); Ok(output.to_string()) }