use chrono::{DateTime, Local, NaiveDateTime, TimeZone}; use rand::{distributions::Alphanumeric, Rng}; #[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 28 Base 62 characters, which corresponds to 166 bits of entropy // https://owasp.org/www-community/vulnerabilities/Insufficient_Session-ID_Length // https://www.rfc-editor.org/rfc/rfc6749.html#section-10.10 const FILE_ID_CHARS: usize = 28; pub fn generate_file_id() -> String { rand::thread_rng() .sample_iter(&Alphanumeric) .take(FILE_ID_CHARS) .map(char::from) .collect() } 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)) } }