use clap::{Parser, Subcommand, ValueEnum};
#[derive(Debug, Clone, ValueEnum)]
pub enum Format {
#[cfg(feature = "srt")]
Srt,
#[cfg(feature = "vtt")]
Vtt,
#[cfg(feature = "ass")]
Ass,
#[cfg(feature = "ssa")]
Ssa,
#[cfg(feature = "microdvd")]
#[value(name = "microdvd")]
MicroDvd,
#[cfg(feature = "subviewer")]
#[value(name = "subviewer")]
SubViewer,
#[cfg(feature = "ttml")]
Ttml,
#[cfg(feature = "sbv")]
Sbv,
#[cfg(feature = "lrc")]
Lrc,
}
impl Format {
pub fn from_ext(path: &str) -> Option<Self> {
let lower = path.to_lowercase();
#[cfg(feature = "srt")]
if lower.ends_with(".srt") {
return Some(Format::Srt);
}
#[cfg(feature = "vtt")]
if lower.ends_with(".vtt") {
return Some(Format::Vtt);
}
#[cfg(feature = "ass")]
if lower.ends_with(".ass") {
return Some(Format::Ass);
}
#[cfg(feature = "ssa")]
if lower.ends_with(".ssa") {
return Some(Format::Ssa);
}
#[cfg(feature = "microdvd")]
if lower.ends_with(".sub") {
return Some(Format::MicroDvd);
}
#[cfg(feature = "ttml")]
if lower.ends_with(".ttml") || lower.ends_with(".xml") {
return Some(Format::Ttml);
}
#[cfg(feature = "sbv")]
if lower.ends_with(".sbv") {
return Some(Format::Sbv);
}
#[cfg(feature = "lrc")]
if lower.ends_with(".lrc") {
return Some(Format::Lrc);
}
None
}
}
impl std::fmt::Display for Format {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
#[cfg(feature = "srt")]
Format::Srt => write!(f, "srt"),
#[cfg(feature = "vtt")]
Format::Vtt => write!(f, "vtt"),
#[cfg(feature = "ass")]
Format::Ass => write!(f, "ass"),
#[cfg(feature = "ssa")]
Format::Ssa => write!(f, "ssa"),
#[cfg(feature = "microdvd")]
Format::MicroDvd => write!(f, "microdvd"),
#[cfg(feature = "subviewer")]
Format::SubViewer => write!(f, "subviewer"),
#[cfg(feature = "ttml")]
Format::Ttml => write!(f, "ttml"),
#[cfg(feature = "sbv")]
Format::Sbv => write!(f, "sbv"),
#[cfg(feature = "lrc")]
Format::Lrc => write!(f, "lrc"),
}
}
}
#[derive(Parser)]
#[command(name = "subtitler")]
#[command(
about = "Subtitle toolkit: parse, convert, validate, edit, and analyze subtitles across 9 formats."
)]
#[command(version)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
Parse(ParseArgs),
Convert(ConvertArgs),
Validate(ValidateArgs),
Edit(EditArgs),
Info(InfoArgs),
Detect(DetectArgs),
Quality(QualityArgs),
Normalize(NormalizeArgs),
Shift(ShiftArgs),
}
#[derive(clap::Args)]
pub struct ParseArgs {
pub input: String,
#[arg(short, long)]
pub format: Option<Format>,
#[arg(short, long)]
pub json: bool,
}
#[derive(clap::Args)]
pub struct ConvertArgs {
pub input: String,
pub output: String,
#[arg(short, long)]
pub from: Option<Format>,
#[arg(short, long)]
pub to: Option<Format>,
#[arg(long)]
pub fps: Option<f64>,
#[arg(long, allow_hyphen_values = true)]
pub shift: Option<i64>,
}
#[derive(clap::Args)]
pub struct ValidateArgs {
pub input: String,
#[arg(long, default_value = "42")]
pub max_chars: usize,
#[arg(long, default_value = "5000")]
pub max_gap: u64,
#[arg(long, default_value = "25.0")]
pub max_cps: f64,
#[arg(long)]
pub basic: bool,
#[arg(short, long)]
pub json: bool,
}
#[derive(clap::Args)]
pub struct EditArgs {
pub input: String,
#[arg(short, long)]
pub output: String,
#[arg(long)]
pub sort: bool,
#[arg(long, allow_hyphen_values = true)]
pub shift: Option<i64>,
#[arg(long)]
pub merge: Option<u64>,
#[arg(long)]
pub split: Option<usize>,
#[arg(long, value_names = &["FROM_FPS", "TO_FPS"], number_of_values = 2)]
pub transform_fps: Option<Vec<f64>>,
#[arg(short, long)]
pub from: Option<Format>,
#[arg(short, long)]
pub to: Option<Format>,
}
#[derive(clap::Args)]
pub struct InfoArgs {
pub input: String,
}
#[derive(clap::Args)]
pub struct DetectArgs {
pub input: String,
}
#[derive(clap::Args)]
pub struct QualityArgs {
pub input: String,
#[arg(long, default_value = "42")]
pub max_chars: usize,
#[arg(long, default_value = "5000")]
pub max_gap: u64,
#[arg(long, default_value = "25.0")]
pub max_cps: f64,
#[arg(short, long)]
pub json: bool,
}
#[derive(clap::Args)]
pub struct NormalizeArgs {
pub input: String,
#[arg(short, long)]
pub output: String,
#[arg(long)]
pub strip_hi: bool,
#[arg(long)]
pub fix_ocr: bool,
#[arg(long)]
pub quotes: bool,
#[arg(long)]
pub whitespace: bool,
#[arg(long)]
pub all: bool,
#[arg(short, long)]
pub format: Option<Format>,
}
#[derive(clap::Args)]
pub struct ShiftArgs {
pub input: String,
#[arg(short, long)]
pub output: String,
#[arg(allow_hyphen_values = true)]
pub offset: i64,
#[arg(short, long)]
pub format: Option<Format>,
}