use std::collections::HashMap;
use std::time::Duration;
use crate::time::Rational;
#[derive(Debug, Clone)]
pub struct ChapterInfo {
id: i64,
title: Option<String>,
start: Duration,
end: Duration,
time_base: Option<Rational>,
metadata: Option<HashMap<String, String>>,
}
impl ChapterInfo {
#[must_use]
pub fn builder() -> ChapterInfoBuilder {
ChapterInfoBuilder::default()
}
#[must_use]
#[inline]
pub fn id(&self) -> i64 {
self.id
}
#[must_use]
#[inline]
pub fn title(&self) -> Option<&str> {
self.title.as_deref()
}
#[must_use]
#[inline]
pub fn start(&self) -> Duration {
self.start
}
#[must_use]
#[inline]
pub fn end(&self) -> Duration {
self.end
}
#[must_use]
#[inline]
pub fn time_base(&self) -> Option<Rational> {
self.time_base
}
#[must_use]
#[inline]
pub fn metadata(&self) -> Option<&HashMap<String, String>> {
self.metadata.as_ref()
}
#[must_use]
#[inline]
pub fn has_title(&self) -> bool {
self.title.is_some()
}
#[must_use]
#[inline]
pub fn duration(&self) -> Duration {
self.end.saturating_sub(self.start)
}
}
impl Default for ChapterInfo {
fn default() -> Self {
Self {
id: 0,
title: None,
start: Duration::ZERO,
end: Duration::ZERO,
time_base: None,
metadata: None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ChapterInfoBuilder {
id: i64,
title: Option<String>,
start: Duration,
end: Duration,
time_base: Option<Rational>,
metadata: Option<HashMap<String, String>>,
}
impl ChapterInfoBuilder {
#[must_use]
pub fn id(mut self, id: i64) -> Self {
self.id = id;
self
}
#[must_use]
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
#[must_use]
pub fn start(mut self, start: Duration) -> Self {
self.start = start;
self
}
#[must_use]
pub fn end(mut self, end: Duration) -> Self {
self.end = end;
self
}
#[must_use]
pub fn time_base(mut self, time_base: Rational) -> Self {
self.time_base = Some(time_base);
self
}
#[must_use]
pub fn metadata(mut self, metadata: HashMap<String, String>) -> Self {
self.metadata = Some(metadata);
self
}
#[must_use]
pub fn build(self) -> ChapterInfo {
ChapterInfo {
id: self.id,
title: self.title,
start: self.start,
end: self.end,
time_base: self.time_base,
metadata: self.metadata,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chapter_info_builder_should_set_all_fields() {
let mut meta = HashMap::new();
meta.insert("language".to_string(), "eng".to_string());
let info = ChapterInfo::builder()
.id(3)
.title("Intro")
.start(Duration::from_secs(0))
.end(Duration::from_secs(60))
.time_base(Rational::new(1, 1000))
.metadata(meta)
.build();
assert_eq!(info.id(), 3);
assert_eq!(info.title(), Some("Intro"));
assert_eq!(info.start(), Duration::ZERO);
assert_eq!(info.end(), Duration::from_secs(60));
assert_eq!(info.time_base(), Some(Rational::new(1, 1000)));
assert_eq!(info.metadata().unwrap()["language"], "eng");
}
#[test]
fn chapter_info_duration_should_return_end_minus_start() {
let info = ChapterInfo::builder()
.start(Duration::from_secs(10))
.end(Duration::from_secs(70))
.build();
assert_eq!(info.duration(), Duration::from_secs(60));
}
#[test]
fn chapter_info_duration_should_return_zero_when_end_before_start() {
let info = ChapterInfo::builder()
.start(Duration::from_secs(70))
.end(Duration::from_secs(10))
.build();
assert_eq!(info.duration(), Duration::ZERO);
}
#[test]
fn chapter_info_with_no_title_should_return_none() {
let info = ChapterInfo::builder().id(1).build();
assert_eq!(info.title(), None);
assert!(!info.has_title());
}
#[test]
fn chapter_info_with_title_should_have_title() {
let info = ChapterInfo::builder().title("Chapter One").build();
assert_eq!(info.title(), Some("Chapter One"));
assert!(info.has_title());
}
#[test]
fn chapter_info_default_should_have_zero_times() {
let info = ChapterInfo::default();
assert_eq!(info.id(), 0);
assert_eq!(info.start(), Duration::ZERO);
assert_eq!(info.end(), Duration::ZERO);
assert!(info.title().is_none());
assert!(info.time_base().is_none());
assert!(info.metadata().is_none());
}
#[test]
fn chapter_info_builder_without_metadata_should_return_none() {
let info = ChapterInfo::builder().id(1).title("Test").build();
assert!(info.metadata().is_none());
}
#[test]
fn chapter_info_builder_clone_should_produce_equal_instance() {
let builder = ChapterInfo::builder()
.id(5)
.title("Cloned")
.start(Duration::from_secs(100))
.end(Duration::from_secs(200));
let info1 = builder.clone().build();
let info2 = builder.build();
assert_eq!(info1.id(), info2.id());
assert_eq!(info1.title(), info2.title());
assert_eq!(info1.start(), info2.start());
assert_eq!(info1.end(), info2.end());
}
}