use core::time::Duration;
use std::io;
use std::path::Path;
use crate::error::FormError;
fn is_spill_name(name: &[u8]) -> bool {
let Some(rest) = name.strip_prefix(b"upload-") else {
return false;
};
let Some(hex) = rest.strip_suffix(b".part") else {
return false;
};
hex.len() == 32
&& hex
.iter()
.all(|&b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
}
fn is_private_dir_name(name: &[u8]) -> bool {
name.strip_prefix(b"form-net-").is_some_and(|hex| {
hex.len() == 32
&& hex
.iter()
.all(|&b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
})
}
pub fn sweep(dir: &Path, older_than: Duration) -> Result<usize, FormError> {
super::temp::check_dir(dir)?;
let io = |e: io::Error| FormError::Io { kind: e.kind() };
let mut removed = 0usize;
for entry in std::fs::read_dir(dir).map_err(io)? {
let Ok(entry) = entry else { continue };
let name = entry.file_name();
let Ok(meta) = entry.metadata() else { continue };
if meta.is_dir() && is_private_dir_name(name.as_encoded_bytes()) {
let inner = sweep_files(&entry.path(), older_than).unwrap_or(0);
removed = removed.saturating_add(inner);
continue;
}
if !is_spill_name(name.as_encoded_bytes()) || meta.is_dir() {
continue;
}
if is_old(&meta, older_than) && std::fs::remove_file(entry.path()).is_ok() {
removed = removed.saturating_add(1);
}
}
Ok(removed)
}
fn is_old(meta: &std::fs::Metadata, older_than: Duration) -> bool {
meta.modified()
.ok()
.and_then(|t| t.elapsed().ok())
.is_some_and(|age| age >= older_than)
}
fn sweep_files(dir: &Path, older_than: Duration) -> Result<usize, FormError> {
super::temp::check_dir(dir)?;
let mut removed = 0usize;
for entry in std::fs::read_dir(dir).map_err(|e| FormError::Io { kind: e.kind() })? {
let Ok(entry) = entry else { continue };
let name = entry.file_name();
if !is_spill_name(name.as_encoded_bytes()) {
continue;
}
let Ok(meta) = entry.metadata() else { continue };
if meta.is_dir() {
continue;
}
if is_old(&meta, older_than) && std::fs::remove_file(entry.path()).is_ok() {
removed = removed.saturating_add(1);
}
}
Ok(removed)
}
#[cfg(test)]
mod tests {
use super::{is_private_dir_name, is_spill_name};
#[test]
fn test_only_private_directory_names_match() {
assert!(is_private_dir_name(
b"form-net-0123456789abcdef0123456789abcdef"
));
for other in [
&b"form-net-"[..],
b"form-net-0123456789ABCDEF0123456789abcdef",
b"form-net-0123456789abcdef0123456789abcde",
b"form-net-0123456789abcdef0123456789abcdef0",
b"form-net-sweep-doc-1",
b"x-form-net-0123456789abcdef0123456789abcdef",
] {
assert!(!is_private_dir_name(other), "{other:?}");
}
}
#[test]
fn test_only_form_net_names_match() {
assert!(is_spill_name(
b"upload-0123456789abcdef0123456789abcdef.part"
));
for other in [
&b"upload-.part"[..],
b"upload-0123456789ABCDEF0123456789abcdef.part",
b"upload-0123456789abcdef0123456789abcde.part",
b"upload-0123456789abcdef0123456789abcdef.part.bak",
b"x-upload-0123456789abcdef0123456789abcdef.part",
b"upload-0123456789abcdef0123456789abcdeg.part",
] {
assert!(!is_spill_name(other), "{other:?}");
}
}
}