use anyhow::{anyhow, Context, Result}; use chrono::{DateTime, Utc}; #[cfg(feature = "tls")] use rustls_pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer}; use std::{ borrow::Cow, path::Path, time::{Duration, SystemTime, UNIX_EPOCH}, }; pub fn unix_now() -> Duration { SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Unable to get unix epoch time") } pub fn encode_uri(v: &str) -> String { let parts: Vec<_> = v.split('/').map(urlencoding::encode).collect(); parts.join("/") } pub fn decode_uri(v: &str) -> Option> { percent_encoding::percent_decode(v.as_bytes()) .decode_utf8() .ok() } pub fn get_file_name(path: &Path) -> &str { path.file_name() .and_then(|v| v.to_str()) .unwrap_or_default() } #[cfg(unix)] pub async fn get_file_mtime_and_mode(path: &Path) -> Result<(DateTime, u16)> { use std::os::unix::prelude::MetadataExt; let meta = tokio::fs::metadata(path).await?; let datetime: DateTime = meta.modified()?.into(); Ok((datetime, meta.mode() as u16)) } #[cfg(not(unix))] pub async fn get_file_mtime_and_mode(path: &Path) -> Result<(DateTime, u16)> { let meta = tokio::fs::metadata(&path).await?; let datetime: DateTime = meta.modified()?.into(); Ok((datetime, 0o644)) } pub fn try_get_file_name(path: &Path) -> Result<&str> { path.file_name() .and_then(|v| v.to_str()) .ok_or_else(|| anyhow!("Failed to get file name of `{}`", path.display())) } pub fn glob(pattern: &str, target: &str) -> bool { let pat = match ::glob::Pattern::new(pattern) { Ok(pat) => pat, Err(_) => return false, }; pat.matches(target) } // Load public certificate from file. #[cfg(feature = "tls")] pub fn load_certs>(file_name: T) -> Result>> { let mut certs = vec![]; for cert in CertificateDer::pem_file_iter(file_name.as_ref()).with_context(|| { format!( "Failed to load cert file at `{}`", file_name.as_ref().display() ) })? { let cert = cert.with_context(|| { format!( "Invalid certificate data in file `{}`", file_name.as_ref().display() ) })?; certs.push(cert) } if certs.is_empty() { anyhow::bail!( "No supported certificate in file `{}`", file_name.as_ref().display() ); } Ok(certs) } // Load private key from file. #[cfg(feature = "tls")] pub fn load_private_key>(file_name: T) -> Result> { PrivateKeyDer::from_pem_file(file_name.as_ref()).with_context(|| { format!( "Failed to load key file at `{}`", file_name.as_ref().display() ) }) } pub fn parse_range(range: &str, size: u64) -> Option> { let (unit, ranges) = range.split_once('=')?; if unit != "bytes" { return None; } let mut result = Vec::new(); for range in ranges.split(',') { let (start, end) = range.trim().split_once('-')?; if start.is_empty() { let offset = end.parse::().ok()?; if offset <= size { result.push((size - offset, size - 1)); } else { return None; } } else { let start = start.parse::().ok()?; if start < size { if end.is_empty() { result.push((start, size - 1)); } else { let end = end.parse::().ok()?; if end < size { result.push((start, end)); } else { return None; } } } else { return None; } } } Some(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_glob_key() { assert!(glob("", "")); assert!(glob(".*", ".git")); assert!(glob("abc", "abc")); assert!(glob("a*c", "abc")); assert!(glob("a?c", "abc")); assert!(glob("a*c", "abbc")); assert!(glob("*c", "abc")); assert!(glob("a*", "abc")); assert!(glob("?c", "bc")); assert!(glob("a?", "ab")); assert!(!glob("abc", "adc")); assert!(!glob("abc", "abcd")); assert!(!glob("a?c", "abbc")); assert!(!glob("*.log", "log")); assert!(glob("*.abc-cba", "xyz.abc-cba")); assert!(glob("*.abc-cba", "123.xyz.abc-cba")); assert!(glob("*.log", ".log")); assert!(glob("*.log", "a.log")); assert!(glob("*/", "abc/")); assert!(!glob("*/", "abc")); } #[test] fn test_parse_range() { assert_eq!(parse_range("bytes=0-499", 500), Some(vec![(0, 499)])); assert_eq!(parse_range("bytes=0-", 500), Some(vec![(0, 499)])); assert_eq!(parse_range("bytes=299-", 500), Some(vec![(299, 499)])); assert_eq!(parse_range("bytes=-500", 500), Some(vec![(0, 499)])); assert_eq!(parse_range("bytes=-300", 500), Some(vec![(200, 499)])); assert_eq!( parse_range("bytes=0-199, 100-399, 400-, -200", 500), Some(vec![(0, 199), (100, 399), (400, 499), (300, 499)]) ); assert_eq!(parse_range("bytes=500-", 500), None); assert_eq!(parse_range("bytes=-501", 500), None); assert_eq!(parse_range("bytes=0-500", 500), None); assert_eq!(parse_range("bytes=0-199,", 500), None); assert_eq!(parse_range("bytes=0-199, 500-", 500), None); } }