use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct FormatId(&'static str);
impl FormatId {
pub const fn new(name: &'static str) -> Self {
Self(name)
}
pub const fn as_str(&self) -> &'static str {
self.0
}
}
impl fmt::Display for FormatId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FormatKind {
Codec,
Container,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum CorruptionDetection {
Always,
WhenPresent,
Structural,
#[default]
Never,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct CodecCaps {
pub encode: bool,
pub decode: bool,
pub parallel_encode: bool,
pub parallel_decode: bool,
pub frame_index: bool,
pub detects_corruption: CorruptionDetection,
pub memory_per_worker: Option<u64>,
pub weak_encoder: bool,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct ContainerCaps {
pub read: bool,
pub write: bool,
pub forward_parse: bool,
pub degraded_parse: bool,
pub trailing_index: bool,
pub solid: bool,
pub per_entry_codec: bool,
pub needs_seek: bool,
pub stores_dirs: bool,
pub stores_symlinks: bool,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct MagicRule {
pub offset: usize,
pub bytes: &'static [u8],
pub format: FormatId,
}
#[derive(Clone, Copy, Debug)]
pub struct FormatMeta {
pub id: FormatId,
pub kind: FormatKind,
pub extensions: &'static [&'static str],
pub magics: &'static [MagicRule],
pub priority: i16,
}
impl CodecCaps {
pub const fn round_trip() -> Self {
Self {
encode: true,
decode: true,
parallel_encode: false,
parallel_decode: false,
frame_index: false,
detects_corruption: CorruptionDetection::Never,
memory_per_worker: None,
weak_encoder: false,
}
}
pub const fn decode_only() -> Self {
Self {
encode: false,
decode: true,
parallel_encode: false,
parallel_decode: false,
frame_index: false,
detects_corruption: CorruptionDetection::Never,
memory_per_worker: None,
weak_encoder: false,
}
}
}
impl ContainerCaps {
pub const fn read_only() -> Self {
Self {
read: true,
write: false,
forward_parse: false,
degraded_parse: false,
trailing_index: false,
solid: false,
per_entry_codec: false,
needs_seek: false,
stores_dirs: false,
stores_symlinks: false,
}
}
pub const fn read_write() -> Self {
let mut c = Self::read_only();
c.write = true;
c
}
}
impl FormatMeta {
pub const fn codec(
id: FormatId,
extensions: &'static [&'static str],
magics: &'static [MagicRule],
) -> Self {
Self {
id,
kind: FormatKind::Codec,
extensions,
magics,
priority: 0,
}
}
pub const fn container(
id: FormatId,
extensions: &'static [&'static str],
magics: &'static [MagicRule],
) -> Self {
Self {
id,
kind: FormatKind::Container,
extensions,
magics,
priority: 0,
}
}
pub const fn with_priority(mut self, priority: i16) -> Self {
self.priority = priority;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_id_round_trips_and_displays() {
let id = FormatId::new("gzip");
assert_eq!(id.as_str(), "gzip");
assert_eq!(id.to_string(), "gzip");
}
#[test]
fn caps_default_to_no_capability() {
let c = CodecCaps::default();
assert!(!c.encode && !c.decode && !c.parallel_encode);
assert_eq!(c.detects_corruption, CorruptionDetection::Never);
let k = ContainerCaps::default();
assert!(!k.read && !k.write && !k.forward_parse && !k.needs_seek);
}
#[test]
fn codec_caps_constructors_express_the_two_common_shapes() {
let rt = CodecCaps::round_trip();
assert!(rt.encode && rt.decode);
assert!(!rt.parallel_encode && !rt.parallel_decode && !rt.frame_index);
let d = CodecCaps::decode_only();
assert!(d.decode && !d.encode);
}
#[test]
fn container_caps_constructors_express_the_two_common_shapes() {
let ro = ContainerCaps::read_only();
assert!(ro.read && !ro.write);
let rw = ContainerCaps::read_write();
assert!(rw.read && rw.write);
assert!(!rw.forward_parse && !rw.trailing_index && !rw.needs_seek);
}
#[test]
fn format_meta_constructors_set_the_kind_for_you() {
const M: &[MagicRule] = &[];
let c = FormatMeta::codec(FormatId::new("gzip"), &["gz"], M);
assert_eq!(c.kind, FormatKind::Codec);
let k = FormatMeta::container(FormatId::new("tar"), &["tar"], M);
assert_eq!(k.kind, FormatKind::Container);
}
}