#[cfg(feature = "infer-backend")] use std::str::FromStr; use mime_guess::Mime; pub trait MimeDb { fn init() -> Self; fn get_type(&self, data: &[u8]) -> Option; } #[cfg(feature = "infer-backend")] pub struct InferDb { db: infer::Infer, } #[cfg(feature = "infer-backend")] impl MimeDb for InferDb { fn init() -> Self { let mut info = infer::Infer::new(); // jpeg2000 support because why the stinch not info.add("image/jpeg2000", ".jp2", |buf| { buf.len() > 23 && buf[..23] == b"\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A\x6A\x70\x32\x20"[..] }); info.add("image/svg+xml", "svg", |buf| { // before doing the moderately expensive SVG check, we should make sure that the input is actually SGML-ish // by "SGML-ish", i mean starts with anywhere from zero to ∞-1 whitespace characters, and then a less than sign, // and then there's some other stuff we don't care about right now // so, here comes our fancy pants """""SGML-ish validator""""" for c in buf { match c { // whitespace (according to https://www.w3.org/TR/xml/#NT-S) b'\t' | b'\r' | b'\n' | b'\x20' => continue, b'<' => break, _ => return false, } } // finally, to check whether or not the file is an SVG: // - split the buffer up into chunks separated by the less than sign // - check to see if this chunk starts with any of these identifiers: let identifiers: Vec<&[u8]> = vec![b"svg", b"SVG", b"!DOCTYPE svg", b"!DOCTYPE SVG"]; // - if it does, the nested `any` will short circuit and immediately return true, causing the parent `any` to do // the same // - and finally, if none of the chunks match, we'll return false // TODO: this is kind of messy, i'd like to clean it up somehow :( buf .split(|c| *c == b'<') .any(|buf| identifiers.iter().any(|id| buf.starts_with(id))) }); // unmut let info = info; Self { db: info } } fn get_type(&self, data: &[u8]) -> Option { self.db.get(data).map(|f| Mime::from_str(f.mime_type()).unwrap()) } } #[cfg(feature = "xdg-mime-backend")] pub struct XdgDb { db: xdg_mime::SharedMimeInfo, } #[cfg(feature = "xdg-mime-backend")] impl MimeDb for XdgDb { fn init() -> Self { Self { db: xdg_mime::SharedMimeInfo::new(), } } fn get_type(&self, data: &[u8]) -> Option { self.db.get_mime_type_for_data(&data).map(|m| m.0) } }