macro_rules! formats {
{
$(
format = $format:ident
name = $name:literal
$(short_name = $short_name:literal)?
media_type = $media_type:literal
extension = $extension:literal
kind = $kind:ident
)*
} => {
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum FileFormat {
$(
#[doc=concat!($name, $(" (", $short_name, ")",)? ".")]
#[doc=concat!("- Media type: `", $media_type, "`")]
#[doc=concat!("- Extension: `.", $extension, "`")]
#[doc=concat!("- Kind: [", stringify!($kind), "](crate::Kind::", stringify!($kind), ")")]
$format,
)*
}
impl crate::FileFormat {
pub const fn name(&self) -> &str {
match self {
$(
Self::$format => $name,
)*
}
}
pub const fn short_name(&self) -> Option<&str> {
match self {
$(
$(Self::$format => Some($short_name),)?
)*
_ => None,
}
}
pub const fn media_type(&self) -> &str {
match self {
$(
Self::$format => $media_type,
)*
}
}
pub const fn extension(&self) -> &str {
match self {
$(
Self::$format => $extension,
)*
}
}
pub const fn kind(&self) -> crate::Kind {
match self {
$(
Self::$format => crate::Kind::$kind,
)*
}
}
pub fn from_kind(kind: crate::Kind) -> &'static [crate::FileFormat] {
static KIND: std::sync::OnceLock<std::collections::HashMap<crate::Kind, Vec<crate::FileFormat>>> =
std::sync::OnceLock::new();
KIND.get_or_init(|| {
let mut map: std::collections::HashMap<crate::Kind, Vec<crate::FileFormat>> =
std::collections::HashMap::new();
$(
map.entry(crate::Kind::$kind).or_default().push(crate::FileFormat::$format);
)*
map
})
.get(&kind)
.map(Vec::as_slice)
.unwrap_or_default()
}
pub fn from_extension(extension: &str) -> &'static [crate::FileFormat] {
static EXTENSION: std::sync::OnceLock<std::collections::HashMap<String, Vec<crate::FileFormat>>> =
std::sync::OnceLock::new();
let extension = extension.strip_prefix('.').unwrap_or(extension);
let lower = extension.to_ascii_lowercase();
EXTENSION.get_or_init(|| {
let mut map: std::collections::HashMap<String, Vec<crate::FileFormat>> =
std::collections::HashMap::new();
$(
map.entry($extension.to_ascii_lowercase()).or_default().push(Self::$format);
)*
map
})
.get(&lower)
.map(Vec::as_slice)
.unwrap_or_default()
}
pub fn from_media_type(media_type: &str) -> &'static [crate::FileFormat] {
static MEDIA_TYPE: std::sync::OnceLock<std::collections::HashMap<&str, Vec<crate::FileFormat>>> =
std::sync::OnceLock::new();
let lower = media_type.to_ascii_lowercase();
MEDIA_TYPE.get_or_init(|| {
let mut map: std::collections::HashMap<&str, Vec<crate::FileFormat>> =
std::collections::HashMap::new();
$(
map.entry($media_type).or_default().push(Self::$format);
)*
map
})
.get(lower.as_str())
.map(Vec::as_slice)
.unwrap_or_default()
}
}
};
}
macro_rules! signatures {
{
$(
format = $format:ident
$(value = $($value:literal $(offset = $offset:literal)?),+)+
)*
} => {
impl crate::FileFormat {
pub(crate) const SIGNATURE_MAX_LEN: usize = {
let sizes: &[usize] = &[
$($($(
$($offset +)? $value.len(),
)+)+)*
];
let mut max = 0;
let mut i = 0;
while i < sizes.len() {
if sizes[i] > max {
max = sizes[i];
}
i += 1;
}
max
};
#[allow(clippy::int_plus_one)]
pub(crate) fn from_signature(bytes: &[u8]) -> Option<Self> {
$(
if $($(bytes.len() >= $($offset +)? $value.len()
&& &bytes[$($offset)?..$($offset +)? $value.len()] == $value)&&*)||* {
return Some(Self::$format);
}
)*
None
}
}
};
}