Skip to main content

subx_cli/core/formats/sub/
mod.rs

1//! MicroDVD/SubViewer SUB subtitle format implementation.
2//!
3//! This module exposes the [`SubFormat`] adapter that implements the
4//! [`SubtitleFormat`] trait. The actual logic is split across:
5//!
6//! - `parser`: parsing logic and the malformed-input disposition matrix
7//! - `serializer`: serialization logic
8//! - `time`: frame ↔ time helpers and shared constants
9//!
10//! # Examples
11//!
12//! ```rust
13//! use subx_cli::core::formats::{SubtitleFormat, sub::SubFormat};
14//! let sub = SubFormat;
15//! let content = "{0}{25}Hello\n";
16//! let subtitle = sub.parse(content).unwrap();
17//! ```
18
19use crate::Result;
20use crate::core::formats::{Subtitle, SubtitleFormat};
21
22mod parser;
23mod serializer;
24mod time;
25
26#[cfg(test)]
27mod tests;
28
29/// Subtitle format implementation for MicroDVD/SubViewer SUB.
30///
31/// The `SubFormat` struct implements parsing, serialization, and
32/// detection for SUB files using frame-based timing. See the
33/// `parser` module documentation for the malformed-input disposition
34/// matrix observed by [`SubFormat::parse`].
35pub struct SubFormat;
36
37impl SubtitleFormat for SubFormat {
38    /// Parse SUB content.
39    ///
40    /// # Malformed-input dispositions
41    ///
42    /// - Empty input → returns [`crate::error::SubXError::SubtitleFormat`].
43    /// - A single cue whose body exceeds the per-cue cap (1 MiB)
44    ///   → returns [`crate::error::SubXError::SubtitleFormat`].
45    /// - Non-numeric frame range (line that does not match
46    ///   `{\d+}{\d+}`) → skipped with a `debug!` log; parsing continues.
47    /// - Frame number that decodes to > 24 h → skipped with a `debug!`
48    ///   log; parsing continues.
49    fn parse(&self, content: &str) -> Result<Subtitle> {
50        parser::parse(content)
51    }
52
53    fn serialize(&self, subtitle: &Subtitle) -> Result<String> {
54        serializer::serialize(subtitle)
55    }
56
57    fn detect(&self, content: &str) -> bool {
58        parser::detect(content)
59    }
60
61    fn format_name(&self) -> &'static str {
62        "SUB"
63    }
64
65    fn file_extensions(&self) -> &'static [&'static str] {
66        &["sub"]
67    }
68}