subx_cli/cli/
mod.rs

1//! Command-line interface for the SubX subtitle processing tool.
2//!
3//! This module provides the top-level CLI application structure and subcommands
4//! for AI-powered matching, subtitle format conversion, audio synchronization,
5//! encoding detection, configuration management, cache operations, and shell
6//! completion generation.
7//!
8//! # Architecture
9//!
10//! The CLI is built using `clap` and follows a subcommand pattern:
11//! - `match` - AI-powered subtitle file matching and renaming
12//! - `convert` - Subtitle format conversion between standards
13//! - `sync` - Audio-subtitle synchronization and timing adjustment
14//! - `detect-encoding` - Character encoding detection and conversion
15//! - `config` - Configuration management and inspection
16//! - `cache` - Cache inspection and dry-run management
17//! - `generate-completion` - Shell completion script generation
18//!
19//! # Examples
20//!
21//! ```bash
22//! # Basic subtitle matching
23//! subx match /path/to/videos /path/to/subtitles
24//!
25//! # Convert SRT to ASS format
26//! subx convert --input file.srt --output file.ass --format ass
27//!
28//! # Detect file encoding
29//! subx detect-encoding *.srt
30//! ```
31
32mod cache_args;
33mod config_args;
34mod convert_args;
35mod detect_encoding_args;
36mod generate_completion_args;
37mod match_args;
38mod sync_args;
39pub mod table;
40pub mod ui;
41
42pub use cache_args::{CacheAction, CacheArgs};
43use clap::{Parser, Subcommand};
44pub use config_args::{ConfigAction, ConfigArgs};
45pub use convert_args::{ConvertArgs, OutputSubtitleFormat};
46pub use detect_encoding_args::DetectEncodingArgs;
47pub use generate_completion_args::GenerateCompletionArgs;
48pub use match_args::MatchArgs;
49pub use sync_args::{SyncArgs, SyncMethod};
50pub use ui::{
51    create_progress_bar, display_ai_usage, display_match_results, print_error, print_success,
52    print_warning,
53};
54
55/// Main CLI application structure defining the top-level interface.
56///
57/// This structure encapsulates the entire command-line interface for SubX,
58/// providing access to all available subcommands and global options.
59///
60/// # Subcommands
61///
62/// - `match` - AI-powered subtitle file matching and intelligent renaming
63/// - `convert` - Format conversion between different subtitle standards
64/// - `sync` - Audio-subtitle synchronization with timing adjustment
65/// - `detect-encoding` - Character encoding detection and conversion
66/// - `config` - Configuration management and inspection utilities
67/// - `generate-completion` - Shell completion script generation
68/// - `cache` - Cache inspection and dry-run management
69///
70/// # Examples
71///
72/// ```rust,no_run
73/// use subx_cli::cli::Cli;
74/// use clap::Parser;
75///
76/// // Parse CLI arguments from specific args instead of std::env
77/// let cli = Cli::parse_from(&["subx", "config", "show"]);
78///
79/// // Access the selected subcommand
80/// match cli.command {
81///     // Handle different commands...
82///     _ => {}
83/// }
84/// ```
85///
86/// # Global Options
87///
88/// Currently, global options are handled within individual subcommands.
89/// Future versions may include global flags such as verbosity control
90/// or configuration file overrides.
91#[derive(Parser, Debug)]
92#[command(name = "subx-cli")]
93#[command(about = "Intelligent subtitle processing CLI tool")]
94#[command(version = env!("CARGO_PKG_VERSION"))]
95pub struct Cli {
96    /// The subcommand to execute.
97    ///
98    /// Each subcommand provides specialized functionality for different
99    /// aspects of subtitle processing and management.
100    #[command(subcommand)]
101    pub command: Commands,
102}
103
104/// Available subcommands for the SubX CLI application.
105///
106/// Each variant represents a specific operation that can be performed
107/// by the SubX tool, with associated argument structures that define
108/// the parameters and options for that operation.
109///
110/// # Command Categories
111///
112/// ## Core Processing Commands
113/// - `Match` - AI-powered subtitle matching and renaming
114/// - `Convert` - Format conversion between subtitle standards
115/// - `Sync` - Audio-subtitle synchronization and timing adjustment
116///
117/// ## Utility Commands  
118/// - `DetectEncoding` - Character encoding detection and conversion
119/// - `Config` - Configuration management and inspection
120/// - `Cache` - Cache management and dry-run inspection
121/// - `GenerateCompletion` - Shell completion script generation
122///
123/// # Examples
124///
125/// ```rust
126/// use subx_cli::cli::{Commands, MatchArgs};
127/// use std::path::PathBuf;
128///
129/// let match_command = Commands::Match(MatchArgs {
130///     path: PathBuf::from("videos/"),
131///     dry_run: true,
132///     confidence: 80,
133///     recursive: false,
134///     backup: false,
135///     copy: false,
136///     move_files: false,
137/// });
138/// ```
139#[derive(Subcommand, Debug)]
140pub enum Commands {
141    /// AI-powered subtitle file matching and intelligent renaming.
142    ///
143    /// Uses artificial intelligence to analyze video and subtitle files,
144    /// matching them based on content similarity and automatically
145    /// renaming subtitle files to match their corresponding videos.
146    Match(MatchArgs),
147
148    /// Convert subtitle files between different formats.
149    ///
150    /// Supports conversion between popular subtitle formats including
151    /// SRT, ASS, VTT, and others with format-specific options.
152    Convert(ConvertArgs),
153
154    /// Detect and convert character encoding of subtitle files.
155    ///
156    /// Automatically detects the character encoding of subtitle files
157    /// and optionally converts them to UTF-8 for better compatibility.
158    DetectEncoding(DetectEncodingArgs),
159
160    /// Synchronize subtitle timing with audio tracks.
161    ///
162    /// Analyzes audio content and adjusts subtitle timing to match
163    /// dialogue and speech patterns in the audio track.
164    Sync(SyncArgs),
165
166    /// Manage and inspect application configuration.
167    ///
168    /// Provides tools for viewing, validating, and managing SubX
169    /// configuration settings from various sources.
170    Config(ConfigArgs),
171
172    /// Generate shell completion scripts.
173    ///
174    /// Creates completion scripts for various shells (bash, zsh, fish)
175    /// to enable tab completion for SubX commands and arguments.
176    GenerateCompletion(GenerateCompletionArgs),
177
178    /// Manage cache and inspect dry-run results.
179    ///
180    /// Provides utilities for examining cached results, clearing cache
181    /// data, and inspecting the effects of dry-run operations.
182    Cache(CacheArgs),
183}
184
185/// Executes the SubX CLI application with parsed arguments.
186///
187/// This is the main entry point for CLI execution, routing parsed
188/// command-line arguments to their respective command handlers.
189///
190/// # Arguments Processing
191///
192/// The function takes ownership of parsed CLI arguments and dispatches
193/// them to the appropriate command implementation based on the selected
194/// subcommand.
195///
196/// # Error Handling
197///
198/// Returns a [`crate::Result<()>`] that wraps any errors encountered
199/// during command execution. Errors are propagated up to the main
200/// function for proper exit code handling.
201///
202/// # Examples
203///
204/// ```rust
205/// use subx_cli::cli::run;
206///
207/// # tokio_test::block_on(async {
208/// // This would typically be called from main()
209/// // run().await?;
210/// # Ok::<(), Box<dyn std::error::Error>>(())
211/// # });
212/// ```
213///
214/// # Async Context
215///
216/// This function is async because several subcommands perform I/O
217/// operations that benefit from async execution, particularly:
218/// - AI service API calls
219/// - Large file processing operations
220/// - Network-based configuration loading
221pub async fn run() -> crate::Result<()> {
222    // Create production configuration service
223    let config_service = std::sync::Arc::new(crate::config::ProductionConfigService::new()?);
224    run_with_config(config_service.as_ref()).await
225}
226
227/// Run the CLI with a provided configuration service.
228///
229/// This function enables dependency injection of configuration services,
230/// making it easier to test and providing better control over configuration
231/// management.
232///
233/// # Arguments
234///
235/// * `config_service` - The configuration service to use
236///
237/// # Errors
238///
239/// Returns an error if command execution fails.
240pub async fn run_with_config(
241    config_service: &dyn crate::config::ConfigService,
242) -> crate::Result<()> {
243    let cli = Cli::parse();
244
245    match cli.command {
246        Commands::Match(args) => {
247            args.validate()
248                .map_err(crate::error::SubXError::CommandExecution)?;
249            crate::commands::match_command::execute(args, config_service).await?;
250        }
251        Commands::Convert(args) => {
252            crate::commands::convert_command::execute(args, config_service).await?;
253        }
254        Commands::Sync(args) => {
255            crate::commands::sync_command::execute(&args, config_service).await?;
256        }
257        Commands::Config(args) => {
258            crate::commands::config_command::execute(args, config_service).await?;
259        }
260        Commands::GenerateCompletion(args) => {
261            let mut cmd = <Cli as clap::CommandFactory>::command();
262            let cmd_name = cmd.get_name().to_string();
263            let mut stdout = std::io::stdout();
264            clap_complete::generate(args.shell, &mut cmd, cmd_name, &mut stdout);
265        }
266        Commands::Cache(args) => {
267            crate::commands::cache_command::execute(args).await?;
268        }
269        Commands::DetectEncoding(args) => {
270            crate::commands::detect_encoding_command::detect_encoding_command(
271                &args.file_paths,
272                args.verbose,
273            )?;
274        }
275    }
276    Ok(())
277}