use base64::{engine::general_purpose::URL_SAFE, Engine as _}; use chrono::{DateTime, Local, NaiveDateTime, TimeZone}; use rand_core::{OsRng, RngCore}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct File { pub id: String, pub name: String, pub expires_at: DateTime, pub content_length: usize, } pub fn local_time() -> DateTime { let dt = Local::now(); match decode_datetime(&encode_datetime(dt)) { Some(res) => res, None => dt, } } // Using 20 bytes (160 bits) to file identifiers // https://owasp.org/www-community/vulnerabilities/Insufficient_Session-ID_Length // https://www.rfc-editor.org/rfc/rfc6749.html#section-10.10 const FILE_ID_BYTES: usize = 20; pub fn generate_file_id() -> String { let mut token = [0u8; FILE_ID_BYTES]; OsRng.fill_bytes(&mut token); URL_SAFE.encode(token) } const FORMAT: &str = "%Y-%m-%d %H:%M:%S"; pub fn encode_datetime(dt: DateTime) -> String { dt.naive_utc().format(FORMAT).to_string() } pub fn decode_datetime(str: &str) -> Option> { let naive_time = NaiveDateTime::parse_from_str(str, FORMAT).ok()?; Some(Local.from_utc_datetime(&naive_time)) } #[cfg(test)] mod tests { use super::*; #[test] fn test_datetime_serialization() { let dt = local_time(); assert_eq!(decode_datetime(&encode_datetime(dt)), Some(dt)) } }