use std::fmt::Debug;
use std::fs;
use std::path::{
Path,
PathBuf,
};
use std::sync::atomic::{
AtomicU64,
Ordering,
};
use crate::{
MimeDetectorCore,
MimeResult,
StreamBasedMimeDetector,
};
pub trait FileBasedMimeDetector: Debug + Send + Sync {
fn core(&self) -> &MimeDetectorCore;
fn max_test_bytes(&self) -> usize;
fn guess_from_filename(&self, filename: &str) -> Vec<String>;
fn guess_from_local_file(&self, file: &Path) -> MimeResult<Vec<String>>;
}
impl<T> StreamBasedMimeDetector for T
where
T: FileBasedMimeDetector,
{
fn core(&self) -> &MimeDetectorCore {
FileBasedMimeDetector::core(self)
}
fn max_test_bytes(&self) -> usize {
FileBasedMimeDetector::max_test_bytes(self)
}
fn guess_from_filename(&self, filename: &str) -> Vec<String> {
FileBasedMimeDetector::guess_from_filename(self, filename)
}
fn guess_from_content_bytes(&self, content: &[u8]) -> MimeResult<Vec<String>> {
with_temp_file(content, |path| {
FileBasedMimeDetector::guess_from_local_file(self, path)
})
}
fn guess_from_file_stream(&self, file: &Path) -> MimeResult<(Vec<String>, Vec<u8>)> {
Ok((
FileBasedMimeDetector::guess_from_local_file(self, file)?,
Vec::new(),
))
}
}
pub(crate) fn with_temp_file<T>(
content: &[u8],
detect: impl FnOnce(&PathBuf) -> MimeResult<T>,
) -> MimeResult<T> {
let path = unique_temp_path("MimeDetectorTemp", ".tmp");
fs::write(&path, content)?;
let result = detect(&path);
let _ = fs::remove_file(&path);
result
}
fn unique_temp_path(prefix: &str, suffix: &str) -> PathBuf {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
std::env::temp_dir().join(format!("{prefix}-{}-{counter}{suffix}", std::process::id()))
}