// sorry clippy, we don't have a choice. askama forces this on us #![allow(clippy::unnecessary_wraps, clippy::trivially_copy_pass_by_ref)] use std::{ borrow::Borrow, fmt::Display, sync::{Arc, LazyLock}, }; use arc_swap::ArcSwap; use rkyv::{ rend::{i32_le, i64_le}, tuple::ArchivedTuple2, }; use time::{OffsetDateTime, UtcOffset, format_description::well_known::Rfc3339}; // pub fn format_time(s: impl Borrow) -> Result { pub fn format_time(s: impl Into) -> Result { let s = s.into().0; (*s.borrow()) .format(&Rfc3339) .map_err(Box::from) .map_err(askama::Error::Custom) } pub fn branch_query(branch: Option<&str>) -> String { if let Some(b) = branch { format!("?h={b}") } else { String::new() } } pub fn timeago(s: impl Into) -> Result { Ok(timeago::Formatter::new() .convert((OffsetDateTime::now_utc() - s.into().0).try_into().unwrap())) } pub fn file_perms(s: u16) -> Result { Ok(unix_mode::to_string(u32::from(s))) } pub struct DisplayHexBuffer(pub const_hex::Buffer); impl Display for DisplayHexBuffer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.0.as_str()) } } pub fn hex(s: &[u8; 20]) -> Result, askama::Error> { let mut buf = const_hex::Buffer::new(); buf.format(s); Ok(DisplayHexBuffer(buf)) } pub fn gravatar(email: &str) -> Result<&'static str, askama::Error> { static CACHE: LazyLock>> = LazyLock::new(|| ArcSwap::new(Arc::new(hashbrown::HashMap::new()))); if let Some(res) = CACHE.load().get(email).copied() { return Ok(res); } let url = format!( "https://www.gravatar.com/avatar/{}", const_hex::encode(md5::compute(email).0) ); let key = Box::leak(Box::from(email)); let url = url.leak(); CACHE.rcu(|curr| { let mut r = (**curr).clone(); r.insert(key, url); r }); Ok(url) } pub struct Timestamp(OffsetDateTime); impl From<&ArchivedTuple2> for Timestamp { fn from(value: &ArchivedTuple2) -> Self { Self( OffsetDateTime::from_unix_timestamp(value.0.to_native()) .unwrap() .to_offset(UtcOffset::from_whole_seconds(value.1.to_native()).unwrap()), ) } } impl From<(i64, i32)> for Timestamp { fn from(value: (i64, i32)) -> Self { Self( OffsetDateTime::from_unix_timestamp(value.0) .unwrap() .to_offset(UtcOffset::from_whole_seconds(value.1).unwrap()), ) } } impl From<&(i64, i32)> for Timestamp { fn from(value: &(i64, i32)) -> Self { Self( OffsetDateTime::from_unix_timestamp(value.0) .unwrap() .to_offset(UtcOffset::from_whole_seconds(value.1).unwrap()), ) } } impl From for Timestamp { fn from(value: OffsetDateTime) -> Self { Self(value) } }