subx_cli/cli/
convert_args.rs

1//! Subtitle format conversion command-line arguments and options.
2//!
3//! This module defines the command-line interface for the `convert` subcommand,
4//! which handles subtitle format conversion between different standards like SRT,
5//! ASS, VTT, and SUB. It provides comprehensive format conversion capabilities
6//! with encoding support and optional file preservation.
7//!
8//! # Supported Formats
9//!
10//! - **SRT (SubRip)**: Most widely used subtitle format
11//! - **ASS (Advanced SubStation Alpha)**: Rich formatting and styling support
12//! - **VTT (WebVTT)**: Web-optimized subtitle format for HTML5 video
13//! - **SUB (MicroDVD/SubViewer)**: Frame-based subtitle format
14//!
15//! # Examples
16//!
17//! ```bash
18//! # Convert SRT to ASS format
19//! subx convert input.srt --format ass --output output.ass
20//!
21//! # Batch convert all SRT files in a directory to VTT
22//! subx convert ./subtitles/ --format vtt
23//!
24//! # Convert with specific encoding
25//! subx convert input.srt --format ass --encoding utf-8 --keep-original
26//! ```
27
28#![allow(clippy::needless_borrows_for_generic_args)]
29// src/cli/convert_args.rs
30use clap::{Args, ValueEnum};
31use std::path::PathBuf;
32
33/// Command-line arguments for subtitle format conversion.
34///
35/// The convert command transforms subtitle files between different formats
36/// while preserving timing information and content structure. It supports
37/// both single file and batch directory processing.
38///
39/// # Examples
40///
41/// ```rust
42/// use subx_cli::cli::ConvertArgs;
43/// use subx_cli::cli::OutputSubtitleFormat;
44/// use std::path::PathBuf;
45///
46/// let args = ConvertArgs {
47///     input: PathBuf::from("input.srt"),
48///     format: Some(OutputSubtitleFormat::Ass),
49///     output: Some(PathBuf::from("output.ass")),
50///     keep_original: true,
51///     encoding: "utf-8".to_string(),
52/// };
53/// ```
54#[derive(Args, Debug)]
55pub struct ConvertArgs {
56    /// Input file or directory path containing subtitle files.
57    ///
58    /// For single file conversion, specify the exact file path.
59    /// For batch processing, specify a directory path and all
60    /// supported subtitle files will be processed.
61    pub input: PathBuf,
62
63    /// Target output format for converted subtitles.
64    ///
65    /// If not specified, the default format from configuration
66    /// will be used. Supported formats include SRT, ASS, VTT, and SUB.
67    ///
68    /// # Examples
69    ///
70    /// ```bash
71    /// --format srt    # Convert to SubRip format
72    /// --format ass    # Convert to Advanced SubStation Alpha
73    /// --format vtt    # Convert to WebVTT format
74    /// --format sub    # Convert to MicroDVD/SubViewer format
75    /// ```
76    #[arg(long, value_enum)]
77    pub format: Option<OutputSubtitleFormat>,
78
79    /// Output file path for the converted subtitle.
80    ///
81    /// If not specified for single file conversion, the output will use
82    /// the same name as input with the appropriate extension.
83    /// For batch processing, files are saved with new extensions in the
84    /// same directory or a format-specific subdirectory.
85    #[arg(short, long)]
86    pub output: Option<PathBuf>,
87
88    /// Preserve the original files after conversion.
89    ///
90    /// By default, original files are preserved. Use this flag to explicitly
91    /// keep originals during batch processing operations.
92    #[arg(long)]
93    pub keep_original: bool,
94
95    /// Character encoding for input and output files.
96    ///
97    /// Specifies the character encoding to use when reading input files
98    /// and writing output files. UTF-8 is the default and recommended
99    /// encoding for maximum compatibility.
100    ///
101    /// # Supported Encodings
102    ///
103    /// - UTF-8 (default, recommended)
104    /// - UTF-16LE, UTF-16BE
105    /// - Windows-1252 (Western European)
106    /// - ISO-8859-1 (Latin-1)
107    /// - GBK, GB2312 (Chinese)
108    /// - Shift_JIS (Japanese)
109    ///
110    /// # Examples
111    ///
112    /// ```bash
113    /// --encoding utf-8        # UTF-8 encoding (default)
114    /// --encoding windows-1252 # Windows Western European
115    /// --encoding gbk          # Chinese GBK encoding
116    /// ```
117    #[arg(long, default_value = "utf-8")]
118    pub encoding: String,
119}
120
121/// Supported output subtitle formats for conversion operations.
122///
123/// This enum defines all subtitle formats that SubX can generate as output.
124/// Each format has specific characteristics and use cases:
125///
126/// - **SRT**: Simple, widely supported, good for basic subtitles
127/// - **ASS**: Advanced formatting, styling, and positioning capabilities
128/// - **VTT**: Web-optimized, supports HTML5 video elements
129/// - **SUB**: Frame-based timing, used in some legacy systems
130///
131/// # Format Characteristics
132///
133/// | Format | Timing | Styling | Web Support | Compatibility |
134/// |--------|--------|---------|-------------|---------------|
135/// | SRT    | Time   | Basic   | Good        | Excellent     |
136/// | ASS    | Time   | Rich    | Limited     | Good          |
137/// | VTT    | Time   | Medium  | Excellent   | Good          |
138/// | SUB    | Frame  | Basic   | Poor        | Limited       |
139///
140/// # Examples
141///
142/// ```rust
143/// use subx_cli::cli::OutputSubtitleFormat;
144///
145/// let srt_format = OutputSubtitleFormat::Srt;
146/// assert_eq!(srt_format.as_str(), "srt");
147/// assert_eq!(srt_format.file_extension(), ".srt");
148/// ```
149#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
150pub enum OutputSubtitleFormat {
151    /// SubRip (.srt) format - most widely supported subtitle format.
152    ///
153    /// Features:
154    /// - Simple time-based format
155    /// - Basic text formatting (bold, italic, underline)
156    /// - Excellent player compatibility
157    /// - Small file size
158    Srt,
159
160    /// Advanced SubStation Alpha (.ass) format - professional subtitle format.
161    ///
162    /// Features:
163    /// - Rich styling and formatting options
164    /// - Precise positioning and animation
165    /// - Multiple font and color support
166    /// - Advanced timing controls
167    Ass,
168
169    /// WebVTT (.vtt) format - web-optimized subtitle format.
170    ///
171    /// Features:
172    /// - HTML5 video element support
173    /// - CSS-like styling capabilities
174    /// - Cue positioning and alignment
175    /// - Web accessibility features
176    Vtt,
177
178    /// MicroDVD/SubViewer (.sub) format - frame-based subtitle format.
179    ///
180    /// Features:
181    /// - Frame-based timing (not time-based)
182    /// - Basic text formatting
183    /// - Legacy format support
184    /// - Compact file structure
185    Sub,
186}
187
188impl OutputSubtitleFormat {
189    /// Returns the format identifier as a string.
190    ///
191    /// This method provides the lowercase string representation of the format,
192    /// which is used for command-line arguments and configuration files.
193    ///
194    /// # Examples
195    ///
196    /// ```rust
197    /// use subx_cli::cli::OutputSubtitleFormat;
198    ///
199    /// assert_eq!(OutputSubtitleFormat::Srt.as_str(), "srt");
200    /// assert_eq!(OutputSubtitleFormat::Ass.as_str(), "ass");
201    /// assert_eq!(OutputSubtitleFormat::Vtt.as_str(), "vtt");
202    /// assert_eq!(OutputSubtitleFormat::Sub.as_str(), "sub");
203    /// ```
204    pub fn as_str(&self) -> &'static str {
205        match self {
206            OutputSubtitleFormat::Srt => "srt",
207            OutputSubtitleFormat::Ass => "ass",
208            OutputSubtitleFormat::Vtt => "vtt",
209            OutputSubtitleFormat::Sub => "sub",
210        }
211    }
212
213    /// Returns the file extension for this format including the dot prefix.
214    ///
215    /// This method provides the standard file extension used for each
216    /// subtitle format, which is useful for generating output filenames.
217    ///
218    /// # Examples
219    ///
220    /// ```rust
221    /// use subx_cli::cli::OutputSubtitleFormat;
222    ///
223    /// assert_eq!(OutputSubtitleFormat::Srt.file_extension(), ".srt");
224    /// assert_eq!(OutputSubtitleFormat::Ass.file_extension(), ".ass");
225    /// assert_eq!(OutputSubtitleFormat::Vtt.file_extension(), ".vtt");
226    /// assert_eq!(OutputSubtitleFormat::Sub.file_extension(), ".sub");
227    /// ```
228    pub fn file_extension(&self) -> &'static str {
229        match self {
230            OutputSubtitleFormat::Srt => ".srt",
231            OutputSubtitleFormat::Ass => ".ass",
232            OutputSubtitleFormat::Vtt => ".vtt",
233            OutputSubtitleFormat::Sub => ".sub",
234        }
235    }
236}
237
238impl std::fmt::Display for OutputSubtitleFormat {
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240        write!(f, "{}", self.as_str())
241    }
242}
243
244// Test parameter parsing behavior
245#[cfg(test)]
246mod tests {
247    use super::*;
248    use crate::cli::{Cli, Commands};
249    use clap::Parser;
250    use std::path::PathBuf;
251
252    #[test]
253    fn test_convert_args_default_values() {
254        let cli = Cli::try_parse_from(&["subx-cli", "convert", "in_path"]).unwrap();
255        let args = match cli.command {
256            Commands::Convert(c) => c,
257            _ => panic!("Expected Convert command"),
258        };
259        assert_eq!(args.input, PathBuf::from("in_path"));
260        assert_eq!(args.format, None);
261        assert_eq!(args.output, None);
262        assert!(!args.keep_original);
263        assert_eq!(args.encoding, "utf-8");
264    }
265
266    #[test]
267    fn test_convert_args_parsing() {
268        let cli = Cli::try_parse_from(&[
269            "subx-cli",
270            "convert",
271            "in",
272            "--format",
273            "vtt",
274            "--output",
275            "out",
276            "--keep-original",
277            "--encoding",
278            "gbk",
279        ])
280        .unwrap();
281        let args = match cli.command {
282            Commands::Convert(c) => c,
283            _ => panic!("Expected Convert command"),
284        };
285        assert_eq!(args.input, PathBuf::from("in"));
286        assert_eq!(args.format.unwrap(), OutputSubtitleFormat::Vtt);
287        assert_eq!(args.output, Some(PathBuf::from("out")));
288        assert!(args.keep_original);
289        assert_eq!(args.encoding, "gbk");
290    }
291}