use super::MimeType;
pub trait MimeCheck: Send + Sync {
fn check(&self, mime_type: &MimeType, bytes: &[u8]) -> Result<(), String>;
fn describe(&self) -> String;
}
impl std::fmt::Debug for dyn MimeCheck {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MimeCheck({})", self.describe())
}
}
pub struct FnCheck<F> {
name: String,
f: F,
}
impl<F> FnCheck<F>
where
F: Fn(&MimeType, &[u8]) -> Result<(), String> + Send + Sync,
{
pub fn new(name: impl Into<String>, f: F) -> Self {
Self {
name: name.into(),
f,
}
}
}
impl<F> MimeCheck for FnCheck<F>
where
F: Fn(&MimeType, &[u8]) -> Result<(), String> + Send + Sync,
{
fn check(&self, mime_type: &MimeType, bytes: &[u8]) -> Result<(), String> {
(self.f)(mime_type, bytes)
}
fn describe(&self) -> String {
self.name.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_closure_check_answers_and_names_itself() {
let check = FnCheck::new("starts-with-a", |t: &MimeType, bytes: &[u8]| {
match bytes.first() {
Some(b'a') => Ok(()),
_ => Err(format!("{t} bytes must start with a")),
}
});
let t = MimeType::parse("text/x-a").unwrap();
assert_eq!(check.check(&t, b"abc"), Ok(()));
assert_eq!(
check.check(&t, b"xyz"),
Err("text/x-a bytes must start with a".to_string())
);
let boxed: std::sync::Arc<dyn MimeCheck> = std::sync::Arc::new(check);
assert_eq!(format!("{boxed:?}"), "MimeCheck(starts-with-a)");
}
}