use std::path::Path;
use super::{Categorization, FileCategorizer};
#[derive(Default)]
pub struct FileCategorizerRegistry {
categorizers: Vec<Box<dyn FileCategorizer>>,
}
impl FileCategorizerRegistry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn register(mut self, c: Box<dyn FileCategorizer>) -> Self {
self.categorizers.push(c);
self
}
#[must_use]
pub fn len(&self) -> usize {
self.categorizers.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.categorizers.is_empty()
}
#[must_use]
pub fn categorize(&self, path: &Path, data: &[u8]) -> Option<Categorization> {
let first = data.first().copied();
for c in &self.categorizers {
if let Some(hint) = c.first_byte_hint() {
if let Some(byte) = first {
if !hint.contains(&byte) {
continue;
}
}
}
if let Some(cat) = c.categorize(path, data) {
return Some(cat);
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
struct AlwaysClaim {
name: &'static str,
codec: u8,
}
impl FileCategorizer for AlwaysClaim {
fn name(&self) -> &'static str {
self.name
}
fn categories(&self) -> &'static [&'static str] {
&["test"]
}
fn categorize(&self, _path: &Path, _data: &[u8]) -> Option<Categorization> {
Some(Categorization {
codec_id: self.codec,
codec_params: Vec::new(),
category: "test",
})
}
}
struct NeverClaim;
impl FileCategorizer for NeverClaim {
fn name(&self) -> &'static str {
"never"
}
fn categories(&self) -> &'static [&'static str] {
&[]
}
fn categorize(&self, _path: &Path, _data: &[u8]) -> Option<Categorization> {
None
}
}
#[test]
fn empty_registry_returns_none() {
let reg = FileCategorizerRegistry::new();
assert!(reg.is_empty());
assert!(reg.categorize(&PathBuf::from("/x"), b"abc").is_none());
}
#[test]
fn first_match_wins() {
let reg = FileCategorizerRegistry::new()
.register(Box::new(AlwaysClaim {
name: "first",
codec: 0x10,
}))
.register(Box::new(AlwaysClaim {
name: "second",
codec: 0x20,
}));
let cat = reg
.categorize(&PathBuf::from("/x"), b"abc")
.expect("first claims");
assert_eq!(cat.codec_id, 0x10);
}
#[test]
fn falls_through_to_next_when_first_passes() {
let reg = FileCategorizerRegistry::new()
.register(Box::new(NeverClaim))
.register(Box::new(AlwaysClaim {
name: "second",
codec: 0x20,
}));
let cat = reg
.categorize(&PathBuf::from("/x"), b"abc")
.expect("second claims");
assert_eq!(cat.codec_id, 0x20);
}
}