#![allow(dead_code)]
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MediaFileKind {
Video,
Audio,
AudioVideo,
StillImage,
Auxiliary,
}
impl MediaFileKind {
#[must_use]
pub fn is_video(self) -> bool {
matches!(self, Self::Video | Self::AudioVideo)
}
#[must_use]
pub fn is_audio(self) -> bool {
matches!(self, Self::Audio | Self::AudioVideo)
}
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::Video => "Video",
Self::Audio => "Audio",
Self::AudioVideo => "AudioVideo",
Self::StillImage => "StillImage",
Self::Auxiliary => "Auxiliary",
}
}
}
#[derive(Debug, Clone)]
pub struct MediaFileRef {
id: u32,
name: String,
path: Option<PathBuf>,
kind: MediaFileKind,
size_bytes: Option<u64>,
}
impl MediaFileRef {
#[must_use]
pub fn new(id: u32, name: impl Into<String>, kind: MediaFileKind) -> Self {
Self {
id,
name: name.into(),
path: None,
kind,
size_bytes: None,
}
}
pub fn set_path(&mut self, path: PathBuf) {
self.path = Some(path);
}
pub fn set_size(&mut self, bytes: u64) {
self.size_bytes = Some(bytes);
}
#[must_use]
pub fn has_path(&self) -> bool {
self.path.is_some()
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn path(&self) -> Option<&PathBuf> {
self.path.as_ref()
}
#[must_use]
pub fn kind(&self) -> MediaFileKind {
self.kind
}
#[must_use]
pub fn id(&self) -> u32 {
self.id
}
#[must_use]
pub fn size_bytes(&self) -> Option<u64> {
self.size_bytes
}
}
#[derive(Debug, Clone, Default)]
pub struct MediaFileRegistry {
entries: HashMap<u32, MediaFileRef>,
next_id: u32,
}
impl MediaFileRegistry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, name: impl Into<String>, kind: MediaFileKind) -> u32 {
let id = self.next_id;
self.next_id += 1;
self.entries.insert(id, MediaFileRef::new(id, name, kind));
id
}
#[must_use]
pub fn find_by_name(&self, name: &str) -> Option<&MediaFileRef> {
self.entries.values().find(|r| r.name() == name)
}
#[must_use]
pub fn find_by_id(&self, id: u32) -> Option<&MediaFileRef> {
self.entries.get(&id)
}
pub fn find_by_id_mut(&mut self, id: u32) -> Option<&mut MediaFileRef> {
self.entries.get_mut(&id)
}
#[must_use]
pub fn count(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn by_kind(&self, kind: MediaFileKind) -> Vec<&MediaFileRef> {
self.entries.values().filter(|r| r.kind() == kind).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_video_kind_is_video() {
assert!(MediaFileKind::Video.is_video());
}
#[test]
fn test_video_kind_not_audio() {
assert!(!MediaFileKind::Video.is_audio());
}
#[test]
fn test_audio_kind_is_audio() {
assert!(MediaFileKind::Audio.is_audio());
}
#[test]
fn test_audiovideo_is_both() {
assert!(MediaFileKind::AudioVideo.is_video());
assert!(MediaFileKind::AudioVideo.is_audio());
}
#[test]
fn test_still_image_not_video() {
assert!(!MediaFileKind::StillImage.is_video());
}
#[test]
fn test_kind_label() {
assert_eq!(MediaFileKind::Video.label(), "Video");
assert_eq!(MediaFileKind::Audio.label(), "Audio");
assert_eq!(MediaFileKind::StillImage.label(), "StillImage");
}
#[test]
fn test_ref_new_no_path() {
let r = MediaFileRef::new(0, "clip.mxf", MediaFileKind::Video);
assert!(!r.has_path());
assert_eq!(r.name(), "clip.mxf");
}
#[test]
fn test_ref_set_path() {
let mut r = MediaFileRef::new(1, "audio.wav", MediaFileKind::Audio);
r.set_path(PathBuf::from("/mnt/media/audio.wav"));
assert!(r.has_path());
}
#[test]
fn test_ref_size() {
let mut r = MediaFileRef::new(2, "img.dpx", MediaFileKind::StillImage);
assert_eq!(r.size_bytes(), None);
r.set_size(1_024_000);
assert_eq!(r.size_bytes(), Some(1_024_000));
}
#[test]
fn test_ref_id() {
let r = MediaFileRef::new(99, "x.mxf", MediaFileKind::Video);
assert_eq!(r.id(), 99);
}
#[test]
fn test_registry_add_increments_count() {
let mut reg = MediaFileRegistry::new();
assert_eq!(reg.count(), 0);
reg.add("a.mxf", MediaFileKind::Video);
reg.add("b.wav", MediaFileKind::Audio);
assert_eq!(reg.count(), 2);
}
#[test]
fn test_registry_find_by_name() {
let mut reg = MediaFileRegistry::new();
reg.add("target.mxf", MediaFileKind::Video);
let found = reg.find_by_name("target.mxf");
assert!(found.is_some());
assert_eq!(found.expect("test expectation failed").name(), "target.mxf");
}
#[test]
fn test_registry_find_by_name_missing() {
let reg = MediaFileRegistry::new();
assert!(reg.find_by_name("ghost.mxf").is_none());
}
#[test]
fn test_registry_find_by_id() {
let mut reg = MediaFileRegistry::new();
let id = reg.add("clip.mov", MediaFileKind::AudioVideo);
assert!(reg.find_by_id(id).is_some());
assert!(reg.find_by_id(id + 999).is_none());
}
#[test]
fn test_registry_find_mut_and_set_path() {
let mut reg = MediaFileRegistry::new();
let id = reg.add("clip.mxf", MediaFileKind::Video);
if let Some(r) = reg.find_by_id_mut(id) {
r.set_path(PathBuf::from("/mnt/clip.mxf"));
}
assert!(reg
.find_by_id(id)
.expect("find_by_id should succeed")
.has_path());
}
#[test]
fn test_registry_by_kind() {
let mut reg = MediaFileRegistry::new();
reg.add("v1.mxf", MediaFileKind::Video);
reg.add("v2.mxf", MediaFileKind::Video);
reg.add("a1.wav", MediaFileKind::Audio);
assert_eq!(reg.by_kind(MediaFileKind::Video).len(), 2);
assert_eq!(reg.by_kind(MediaFileKind::Audio).len(), 1);
}
}