use std::ops::Range;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FilterKind {
Delta { channels: usize },
E8,
E8E9,
Arm,
Itanium,
Rgb { width: usize, pos_r: usize },
Audio { channels: usize },
}
impl FilterKind {
pub const fn name(self) -> &'static str {
match self {
Self::Delta { .. } => "the delta filter",
Self::E8 => "the x86 E8 filter",
Self::E8E9 => "the x86 E8/E9 filter",
Self::Arm => "the ARM filter",
Self::Itanium => "the Itanium filter",
Self::Rgb { .. } => "the RGB filter",
Self::Audio { .. } => "the audio filter",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FilterSpec {
pub kind: FilterKind,
pub range: Option<Range<usize>>,
}
impl FilterSpec {
pub const fn whole(kind: FilterKind) -> Self {
Self { kind, range: None }
}
pub const fn range(kind: FilterKind, range: Range<usize>) -> Self {
Self {
kind,
range: Some(range),
}
}
}
impl From<FilterKind> for FilterSpec {
fn from(kind: FilterKind) -> Self {
Self::whole(kind)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum FilterPolicy {
#[default]
None,
Auto,
Explicit(FilterSpec),
}
impl FilterPolicy {
pub const fn explicit(kind: FilterKind) -> Self {
Self::Explicit(FilterSpec::whole(kind))
}
}
impl FilterKind {
pub fn is_supported_by(self, target: crate::ArchiveVersion) -> bool {
match target.family() {
crate::version::ArchiveFamily::Rar50Plus => {
crate::codec::rar50::Rar50Filter::try_from(self).is_ok()
}
crate::version::ArchiveFamily::Rar15To40 => {
!matches!(
target,
crate::ArchiveVersion::Rar15 | crate::ArchiveVersion::Rar20
) && crate::codec::rar29::Rar29Filter::try_from(self).is_ok()
}
crate::version::ArchiveFamily::Rar13 => false,
}
}
}
pub fn formats_supporting_filter(kind: FilterKind) -> Vec<crate::ArchiveVersion> {
crate::ArchiveVersion::ALL
.into_iter()
.filter(|&target| kind.is_supported_by(target))
.collect()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UnsupportedFilterKind(pub FilterKind);
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::rar29::Rar29Filter;
use crate::codec::rar50::Rar50Filter;
const ALL: [FilterKind; 7] = [
FilterKind::Delta { channels: 1 },
FilterKind::E8,
FilterKind::E8E9,
FilterKind::Arm,
FilterKind::Itanium,
FilterKind::Rgb { width: 3, pos_r: 0 },
FilterKind::Audio { channels: 1 },
];
#[test]
fn every_filter_is_named_and_accepted_by_at_least_one_format() {
for kind in ALL {
assert!(
!kind.name().is_empty(),
"{kind:?} has no name to refuse it by"
);
assert!(
Rar50Filter::try_from(kind).is_ok() || Rar29Filter::try_from(kind).is_ok(),
"{kind:?} is a filter no format can write"
);
}
}
#[test]
fn the_two_families_each_have_what_the_other_lacks() {
assert!(Rar50Filter::try_from(FilterKind::Arm).is_ok());
assert!(Rar29Filter::try_from(FilterKind::Arm).is_err());
for kind in [
FilterKind::Itanium,
FilterKind::Rgb { width: 3, pos_r: 0 },
FilterKind::Audio { channels: 1 },
] {
assert!(Rar29Filter::try_from(kind).is_ok(), "{kind:?}");
assert_eq!(
Rar50Filter::try_from(kind),
Err(UnsupportedFilterKind(kind)),
"{kind:?}"
);
}
}
#[test]
fn each_filter_names_the_formats_that_can_write_it() {
use crate::ArchiveVersion;
assert_eq!(
formats_supporting_filter(FilterKind::Arm),
vec![ArchiveVersion::Rar50, ArchiveVersion::Rar70]
);
assert_eq!(
formats_supporting_filter(FilterKind::Itanium),
vec![
ArchiveVersion::Rar29,
ArchiveVersion::Rar30,
ArchiveVersion::Rar40
]
);
assert_eq!(
formats_supporting_filter(FilterKind::Delta { channels: 2 }),
vec![
ArchiveVersion::Rar29,
ArchiveVersion::Rar30,
ArchiveVersion::Rar40,
ArchiveVersion::Rar50,
ArchiveVersion::Rar70
]
);
for kind in ALL {
assert!(!formats_supporting_filter(kind).is_empty(), "{kind:?}");
}
}
#[test]
fn a_policy_defaults_to_leaving_the_data_alone() {
assert_eq!(FilterPolicy::default(), FilterPolicy::None);
assert_eq!(
FilterPolicy::explicit(FilterKind::E8),
FilterPolicy::Explicit(FilterSpec::whole(FilterKind::E8))
);
}
}