//! Mime mmagic analogous to [`magic::Cookie`] BUT [`Sync`] // I don't think I can do better with thread exclusive `libmagic` use std::cell::OnceCell; use magic::{Cookie, cookie}; #[derive(Debug, thiserror::Error)] pub enum NewCookieError { #[error(transparent)] MimeCookieOpen(#[from] magic::cookie::OpenError), #[error(transparent)] MimeDBLoad(#[from] magic::cookie::LoadError), // #[error(transparent)] // MimeDBPath(#[from] magic::cookie::InvalidDatabasePathError), } pub struct Mime { magic_dbs: magic::cookie::DatabasePaths, } impl Mime { thread_local! { static COOKIE_CELL: OnceCell> = const { OnceCell::new() }; } fn use_cookie(&self, f: F) -> Result where F: FnOnce(&Cookie) -> T, { Self::COOKIE_CELL.with(|cookie| { let may_cookie = cookie.get_or_try_init::<_, NewCookieError>(move || { let cookie = magic::Cookie::open(magic::cookie::Flags::MIME)?; Ok(cookie.load(&self.magic_dbs)?) }); match may_cookie { Ok(c) => Ok(f(c)), Err(e) => Err(e), } }) } #[must_use] pub const fn new(magic_dbs: magic::cookie::DatabasePaths) -> Self { Self { magic_dbs } } /// Cookie initialization is delayed, so each call might be the creation of the cookie if its /// the first use of the cookie in this thread. /// /// # Errors /// /// First layer error fails if new cookie creation failed. Second layer error represents if the /// mime search was successful. pub fn buffer( &self, buffer: &[u8], ) -> Result, NewCookieError> { self.use_cookie(|c| c.buffer(buffer)) } } // fn __ace(c: Mime) { // fn __ace_inner(c: impl Sync + Send) {} // __ace_inner(c); // }