subx_cli/cli/
table.rs

1//! Table formatting utilities for displaying structured CLI output.
2//!
3//! This module provides specialized table formatting capabilities for displaying
4//! various types of structured data in the SubX CLI. It focuses primarily on
5//! match operation results but can be extended for other tabular data needs.
6//!
7//! # Features
8//!
9//! - **Match Results Display**: Formatted tables for AI matching operations
10//! - **Consistent Styling**: Rounded borders and aligned columns
11//! - **Internationalization**: Support for Chinese column headers
12//! - **Flexible Layout**: Automatic column width adjustment
13//!
14//! # Table Styling
15//!
16//! All tables use a consistent rounded border style with left-aligned content
17//! for optimal readability. The styling is designed to work well in both
18//! light and dark terminal themes.
19//!
20//! # Examples
21//!
22//! ```rust
23//! use subx_cli::cli::table::{MatchDisplayRow, create_match_table};
24//!
25//! // Each match result is split into multiple lines for display: video, subtitle and new name
26//! let rows = vec![
27//!     MatchDisplayRow {
28//!         file_type: "Video 1".to_string(),
29//!         file_path: "movie.mp4".to_string(),
30//!     },
31//!     MatchDisplayRow {
32//!         file_type: "Subtitle 1".to_string(),
33//!         file_path: "subtitle.srt".to_string(),
34//!     },
35//!     MatchDisplayRow {
36//!         file_type: "New name 1".to_string(),
37//!         file_path: "movie.srt".to_string(),
38//!     },
39//! ];
40//!
41//! let table = create_match_table(rows);
42//! println!("{}", table);
43//! ```
44
45use tabled::settings::{Alignment, Modify, Style, object::Rows};
46use tabled::{Table, Tabled};
47
48/// Display row structure for file matching operation results.
49///
50/// This structure represents a single row in the match results table,
51/// containing file type identifier and complete file path. Each row shows
52/// one piece of information from an AI-powered file matching operation,
53/// with multiple rows grouped together to represent one complete match result.
54///
55/// # Field Descriptions
56///
57/// - `file_type`: File type identifier (Video 1, Subtitle 1, New name 1)
58/// - `file_path`: Complete file path displayed without truncation
59///
60/// # File Type Identifiers
61///
62/// Standard file type values:
63/// - `Video 1`, `Video 2`, etc.: Original video files used as reference
64/// - `Subtitle 1`, `Subtitle 2`, etc.: Original subtitle files to be renamed
65/// - `New name 1`, `New name 2`, etc.: Generated new names for subtitle files
66///
67/// # Examples
68///
69/// ```rust
70/// use subx_cli::cli::table::MatchDisplayRow;
71///
72/// // Video file entry
73/// let video_row = MatchDisplayRow {
74///     file_type: "Video 1".to_string(),
75///     file_path: "/path/to/Movie.2023.1080p.BluRay.mp4".to_string(),
76/// };
77///
78/// // Subtitle file entry
79/// let subtitle_row = MatchDisplayRow {
80///     file_type: "Subtitle 1".to_string(),
81///     file_path: "/path/to/random_subtitle.srt".to_string(),
82/// };
83///
84/// // New name entry
85/// let newname_row = MatchDisplayRow {
86///     file_type: "New name 1".to_string(),
87///     file_path: "/path/to/Movie.2023.1080p.BluRay.srt".to_string(),
88/// };
89/// ```
90#[derive(Tabled)]
91/// Match result table row for displaying file type and path in clean two-column layout
92pub struct MatchDisplayRow {
93    /// File type identifier (Video 1, Subtitle 1, New name 1)
94    #[tabled(rename = "Type")]
95    pub file_type: String,
96
97    /// Complete file path without truncation
98    #[tabled(rename = "Path")]
99    pub file_path: String,
100}
101
102/// Create a formatted table string from match operation results.
103///
104/// Transforms a collection of match display rows into a beautifully formatted
105/// table string suitable for terminal display. The table uses consistent
106/// styling with rounded borders and proper column alignment for optimal
107/// readability.
108///
109/// # Table Features
110///
111/// - **Rounded borders**: Modern, visually appealing table style
112/// - **Left alignment**: Consistent text alignment for all content rows
113/// - **Auto-sizing**: Columns automatically adjust to content width
114/// - **Header styling**: Clear distinction between headers and data
115/// - **Unicode support**: Proper handling of Chinese characters and symbols
116///
117/// # Arguments
118///
119/// * `rows` - Vector of `MatchDisplayRow` structures to be displayed
120///
121/// # Returns
122///
123/// A formatted table string ready for printing to the terminal
124///
125/// # Examples
126///
127/// ```rust
128/// use subx_cli::cli::table::{MatchDisplayRow, create_match_table};
129///
130/// // Multi-line display of multiple match results
131/// let results = vec![
132///     MatchDisplayRow { file_type: "Video 1".to_string(), file_path: "Movie.mp4".to_string() },
133///     MatchDisplayRow { file_type: "Subtitle 1".to_string(), file_path: "sub123.srt".to_string() },
134///     MatchDisplayRow { file_type: "New name 1".to_string(), file_path: "Movie.srt".to_string() },
135///     MatchDisplayRow { file_type: "Video 2".to_string(), file_path: "Episode.mkv".to_string() },
136///     MatchDisplayRow { file_type: "Subtitle 2".to_string(), file_path: "unknown.srt".to_string() },
137///     MatchDisplayRow { file_type: "New name 2".to_string(), file_path: "Episode.srt".to_string() },
138/// ];
139///
140/// let table = create_match_table(results);
141/// println!("{}", table);
142/// ```
143///
144/// # Output Example
145///
146/// ```text
147/// ╭──────┬──────────────┬──────────────┬──────────────╮
148/// │ Status │ Video File     │ Subtitle File     │ New Name       │
149/// ├──────┼──────────────┼──────────────┼──────────────┤
150/// │ ✓    │ Movie.mp4    │ sub123.srt   │ Movie.srt    │
151/// │ ⚠    │ Episode.mkv  │ unknown.srt  │ Episode.srt  │
152/// ╰──────┴──────────────┴──────────────┴──────────────╯
153/// ```
154///
155/// # Empty Input Handling
156///
157/// If an empty vector is provided, returns a table with only headers,
158/// indicating no results to display.
159pub fn create_match_table(rows: Vec<MatchDisplayRow>) -> String {
160    let mut table = Table::new(rows);
161    table
162        .with(Style::rounded())
163        .with(Modify::new(Rows::new(1..)).with(Alignment::left()));
164    table.to_string()
165}