use super::{Marker, MarkerId, MarkerType};
use crate::clip::ClipId;
use crate::error::{ClipError, ClipResult};
use std::collections::HashMap;
#[derive(Debug, Clone, Default)]
pub struct MarkerManager {
markers: HashMap<ClipId, Vec<Marker>>,
}
impl MarkerManager {
#[must_use]
pub fn new() -> Self {
Self {
markers: HashMap::new(),
}
}
pub fn add_marker(&mut self, clip_id: ClipId, marker: Marker) {
self.markers.entry(clip_id).or_default().push(marker);
}
pub fn remove_marker(&mut self, clip_id: &ClipId, marker_id: &MarkerId) -> ClipResult<()> {
if let Some(markers) = self.markers.get_mut(clip_id) {
if let Some(pos) = markers.iter().position(|m| &m.id == marker_id) {
markers.remove(pos);
return Ok(());
}
}
Err(ClipError::MarkerNotFound(marker_id.to_string()))
}
#[must_use]
pub fn get_markers(&self, clip_id: &ClipId) -> Vec<&Marker> {
self.markers
.get(clip_id)
.map_or_else(Vec::new, |markers| markers.iter().collect())
}
#[must_use]
pub fn get_markers_by_type(&self, clip_id: &ClipId, marker_type: MarkerType) -> Vec<&Marker> {
self.markers.get(clip_id).map_or_else(Vec::new, |markers| {
markers
.iter()
.filter(|m| m.marker_type == marker_type)
.collect()
})
}
#[must_use]
pub fn get_marker(&self, clip_id: &ClipId, marker_id: &MarkerId) -> Option<&Marker> {
self.markers
.get(clip_id)
.and_then(|markers| markers.iter().find(|m| &m.id == marker_id))
}
pub fn update_marker(
&mut self,
clip_id: &ClipId,
marker_id: &MarkerId,
updated: Marker,
) -> ClipResult<()> {
if let Some(markers) = self.markers.get_mut(clip_id) {
if let Some(marker) = markers.iter_mut().find(|m| &m.id == marker_id) {
*marker = updated;
return Ok(());
}
}
Err(ClipError::MarkerNotFound(marker_id.to_string()))
}
#[must_use]
pub fn get_chapters(&self, clip_id: &ClipId) -> Vec<&Marker> {
let mut chapters = self.get_markers_by_type(clip_id, MarkerType::Chapter);
chapters.sort_by_key(|m| m.frame);
chapters
}
#[must_use]
pub fn get_todos(&self, clip_id: &ClipId) -> Vec<&Marker> {
self.get_markers_by_type(clip_id, MarkerType::ToDo)
}
#[must_use]
pub fn get_incomplete_todos(&self, clip_id: &ClipId) -> Vec<&Marker> {
self.get_markers_by_type(clip_id, MarkerType::ToDo)
.into_iter()
.filter(|m| !m.is_completed)
.collect()
}
pub fn clear_markers(&mut self, clip_id: &ClipId) {
self.markers.remove(clip_id);
}
#[must_use]
pub fn total_markers(&self) -> usize {
self.markers.values().map(Vec::len).sum()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_marker_manager() {
let mut manager = MarkerManager::new();
let clip_id = ClipId::new();
let marker1 = Marker::chapter(100, "Chapter 1");
let marker2 = Marker::chapter(200, "Chapter 2");
let marker3 = Marker::todo(150, "Fix color");
manager.add_marker(clip_id, marker1.clone());
manager.add_marker(clip_id, marker2.clone());
manager.add_marker(clip_id, marker3);
assert_eq!(manager.get_markers(&clip_id).len(), 3);
assert_eq!(manager.get_chapters(&clip_id).len(), 2);
assert_eq!(manager.get_todos(&clip_id).len(), 1);
}
#[test]
fn test_remove_marker() {
let mut manager = MarkerManager::new();
let clip_id = ClipId::new();
let marker = Marker::chapter(100, "Chapter 1");
let marker_id = marker.id;
manager.add_marker(clip_id, marker);
assert_eq!(manager.get_markers(&clip_id).len(), 1);
manager
.remove_marker(&clip_id, &marker_id)
.expect("remove_marker should succeed");
assert_eq!(manager.get_markers(&clip_id).len(), 0);
}
}