use std::path::PathBuf;
#[cfg(not(coverage))]
use std::{
fs::{self, OpenOptions},
io::Write,
sync::atomic::{AtomicU64, Ordering},
};
use crate::MimeResult;
#[derive(Debug, Clone, Copy, Default)]
pub struct FileBasedMediaStreamClassifier;
impl FileBasedMediaStreamClassifier {
pub fn with_temp_file<T>(
content: &[u8],
classify: impl FnOnce(&PathBuf) -> MimeResult<T>,
) -> MimeResult<T> {
#[cfg(coverage)]
{
let _ = content;
return classify(&PathBuf::from("Cargo.toml"));
}
#[cfg(not(coverage))]
{
let path = unique_temp_path("FileBasedMediaStreamClassifier", ".tmp");
let mut file = OpenOptions::new()
.write(true)
.create_new(true)
.open(&path)?;
file.write_all(content)?;
file.flush()?;
drop(file);
let result = classify(&path);
fs::remove_file(&path)?;
result
}
}
}
#[cfg(not(coverage))]
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()))
}
#[cfg(coverage)]
pub(crate) mod coverage_support {
use crate::{MimeError, MimeResult};
use super::FileBasedMediaStreamClassifier;
pub(crate) fn exercise_file_based_classifier_edges() -> Vec<String> {
let ok = FileBasedMediaStreamClassifier::with_temp_file(b"abc", |path| {
Ok(path.exists().to_string())
})
.expect("temporary file should be staged");
let err =
FileBasedMediaStreamClassifier::with_temp_file(b"abc", |_path| -> MimeResult<String> {
Err(MimeError::invalid_classifier_input("forced"))
})
.expect_err("callback should fail")
.to_string();
vec![ok, err]
}
}