bestls 1.5.0

A fast and colorful Rust-based ls replacement CLI tool with JSON output and sorting options.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
//! # Command Line Interface Module
//!
//! This module defines the command-line interface for **bestls** using the `clap` crate.
//! It provides a comprehensive CLI with support for various output formats, sorting options,
//! and shell completion generation.
//!
//! ## Key Components
//!
//! - [`Cli`]: Main command-line interface structure
//! - [`SortBy`]: Enumeration for sorting options
//! - [`Commands`][]: Subcommands (currently just completion generation)
//!
//! ## Features
//!
//! * **Flexible Output**: Table format (default), compact JSON, or pretty-printed JSON
//! * **Sorting Options**: Sort by file name, size, or modification date
//! * **Hidden Files**: Option to include or exclude hidden files (starting with '.')
//! * **Shell Completions**: Generate completions for bash, zsh, and fish shells
//!
//! ## Examples
//!
//! ### Basic Usage
//!
//! ```bash
//! # List current directory (default behavior)
//! bestls
//!
//! # List specific directory
//! bestls -p /home/user/documents
//!
//! # Include hidden files
//! bestls -a
//!
//! # Sort by file size
//! bestls --sort size
//!
//! # Output as pretty JSON
//! bestls --json-pretty
//! ```
//!
//! ### Shell Completion Generation
//!
//! ```bash
//! # Generate bash completions
//! bestls completion bash > ~/.local/share/bash-completion/completions/bestls
//!
//! # Generate zsh completions
//! bestls completion zsh > ~/.zfunc/_bestls
//!
//! # Generate fish completions
//! bestls completion fish > ~/.config/fish/completions/bestls.fish
//! ```

use clap::{CommandFactory, Parser, ValueEnum};
use clap_complete::{generate, Shell};
use std::io;
use std::path::PathBuf;

/// Main command-line interface structure for bestls.
///
/// This struct defines all available command-line arguments and options using `clap`'s derive API.
/// It provides a comprehensive interface for file listing with various output formats and sorting options.
///
/// # Arguments
///
/// * `command` - Optional subcommand (currently only completion generation)
/// * `path` - Directory path to list (defaults to current directory)
/// * `json` - Output in compact JSON format
/// * `json_pretty` - Output in pretty-printed JSON format
/// * `sort_by` - Sort files by name, size, or modification date
/// * `all` - Include hidden files in the listing
///
/// # Examples
///
/// The CLI structure automatically handles argument parsing and validation:
///
/// ```rust
/// use clap::Parser;
/// use bestls::cli::Cli;
///
/// // Parse command-line arguments
/// let cli = Cli::parse();
///
/// // Access parsed values
/// let include_hidden = cli.all;
/// let path = cli.path.unwrap_or_else(|| std::path::PathBuf::from("."));
/// ```
///
/// # Shell Integration
///
/// When used as a CLI tool, the help output provides comprehensive usage information:
///
/// ```text
/// bestls is a Rust-powered file listing CLI tool.
///
/// Features:
/// - Outputs in table or JSON formats.
/// - Supports sorting by name, size, or modification date.
/// - Pretty-printed JSON output available.
/// - Shell completion generation support.
///
/// Enumeration of available output formats.
///
/// This enum defines the different output formats supported by bestls.
/// It uses `clap`'s `ValueEnum` derive to automatically generate command-line
/// value parsing and validation.
///
/// # Variants
///
/// * `Table` - Pretty table format (default)
/// * `Json` - Compact JSON format
/// * `JsonPretty` - Pretty-printed JSON format
#[derive(Debug, Clone, Copy, ValueEnum)]
#[clap(rename_all = "kebab-case")]
pub enum OutputFormat {
    /// Pretty table format (default)
    #[value(name = "table")]
    Table,
    /// Compact JSON format
    #[value(name = "json")]
    Json,
    /// Pretty-printed JSON format
    #[value(name = "json-pretty")]
    JsonPretty,
}

#[derive(Debug, Parser)]
#[command(
    version,
    about = "Rust based LS command",
    long_about = r#"bestls is a Rust-powered file listing CLI tool.

Features:
- Outputs in table or JSON formats.
- Supports sorting by name, size, or modification date.
- Pretty-printed JSON output available.
- Shell completion generation support.

Usage Examples:
  bestls -p ./src
  bestls --json --sort size
  bestls --json-pretty --sort date
  bestls completion bash > ~/.local/share/bash-completion/completions/bestls
"#
)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Option<Commands>,
    #[arg(
        short = 'p',
        long = "path",
        value_name = "PATH",
        help = "Directory path to list files from. Defaults to current directory."
    )]
    pub path: Option<PathBuf>,

    #[arg(
        short = 'j',
        long = "json",
        help = "Output file list in compact JSON format (deprecated, use --format json instead).",
        default_value_t = false
    )]
    pub json: bool,

    #[arg(
        long = "json-pretty",
        help = "Output file list in pretty-printed JSON format (deprecated, use --format json-pretty instead).",
        default_value_t = false
    )]
    pub json_pretty: bool,

    #[arg(
        short = 's',
        long = "sort",
        value_enum,
        default_value = "name",
        help = "Sort files by the given attribute."
    )]
    pub sort_by: SortBy,

    #[arg(
        short = 'a',
        long = "all",
        help = "Include hidden files.",
        default_value_t = false
    )]
    pub all: bool,

    #[arg(
        long = "compact",
        help = "Output in compact single-column format.",
        default_value_t = false
    )]
    pub compact: bool,

    #[arg(
        long = "columns",
        value_name = "COLS",
        help = "Comma-separated columns to display: name,type,size,date,permissions,owner,group"
    )]
    pub columns: Option<String>,

    #[arg(
        long = "out",
        value_name = "FILE",
        help = "Export output to file instead of stdout."
    )]
    pub output_file: Option<std::path::PathBuf>,

    #[arg(
        long = "format",
        value_name = "FORMAT",
        value_enum,
        default_value = "table",
        help = "Output format: table, json, or json-pretty (legacy --json/--json-pretty flags override this for backward compatibility)"
    )]
    pub format: OutputFormat,

    #[arg(
        long = "no-color",
        help = "Disable colored output.",
        default_value_t = false
    )]
    pub no_color: bool,

    #[arg(
        long = "tree",
        help = "Display directory tree (recursive listing).",
        default_value_t = false
    )]
    pub tree: bool,

    #[arg(
        long = "depth",
        value_name = "N",
        requires = "tree",
        help = "Maximum depth for tree traversal (requires --tree)."
    )]
    pub depth: Option<usize>,

    #[arg(
        long = "filter-ext",
        value_name = "EXT",
        help = "Filter by file extension (e.g., rs,txt,md). Comma-separated list."
    )]
    pub filter_ext: Option<String>,

    #[arg(
        long = "filter-name",
        value_name = "PATTERN",
        help = "Filter by filename pattern (glob-style, e.g., '*.txt')."
    )]
    pub filter_name: Option<String>,

    #[arg(
        long = "min-size",
        value_name = "SIZE",
        help = "Filter files with minimum size (e.g., 1KB, 1MB, 100B)."
    )]
    pub min_size: Option<String>,

    #[arg(
        long = "max-size",
        value_name = "SIZE",
        help = "Filter files with maximum size (e.g., 1KB, 1MB, 100B)."
    )]
    pub max_size: Option<String>,
}

/// Enumeration of available sorting options for file listings.
///
/// This enum defines the different ways files can be sorted in the output.
/// It uses `clap`'s `ValueEnum` derive to automatically generate command-line
/// value parsing and validation.
///
/// # Variants
///
/// * `Name` - Sort files alphabetically by filename (default)
/// * `Size` - Sort files by size in bytes (smallest to largest)
/// * `Date` - Sort files by modification date (oldest to newest)
///
/// # Examples
///
/// ```rust
/// use bestls::cli::SortBy;
/// use clap::ValueEnum;
///
/// // Parse from command line value
/// let sort_option = SortBy::from_str("size", true).unwrap();
/// match sort_option {
///     SortBy::Name => println!("Sorting by name"),
///     SortBy::Size => println!("Sorting by size"),
///     SortBy::Date => println!("Sorting by date"),
/// }
/// ```
///
/// # CLI Usage
///
/// ```bash
/// # Sort by name (default)
/// bestls
///
/// # Sort by file size
/// bestls --sort size
///
/// # Sort by modification date
/// bestls --sort date
/// ```
#[derive(Debug, Clone, ValueEnum)]
#[clap(rename_all = "lower")]
pub enum SortBy {
    /// Sort files alphabetically by filename
    Name,
    /// Sort files by size in bytes (smallest to largest)
    Size,
    /// Sort files by modification date (oldest to newest)
    Date,
}

/// Available subcommands for the bestls CLI.
///
/// Currently, the only subcommand is completion generation for various shells.
/// This enum uses `clap`'s derive API to automatically handle subcommand parsing
/// and help text generation.
///
/// # Subcommands
///
/// * `Completion` - Generate shell completion scripts
///
/// # Examples
///
/// ```bash
/// # Generate bash completions
/// bestls completion bash
///
/// # Generate zsh completions  
/// bestls completion zsh
///
/// # Generate fish completions
/// bestls completion fish
/// ```
///
/// # Installation
///
/// After generating completion scripts, install them to the appropriate location:
///
/// ```bash
/// # Bash
/// bestls completion bash > ~/.local/share/bash-completion/completions/bestls
///
/// # Zsh (ensure ~/.zfunc is in your $fpath)
/// bestls completion zsh > ~/.zfunc/_bestls
///
/// # Fish
/// bestls completion fish > ~/.config/fish/completions/bestls.fish
/// ```
#[derive(Debug, Parser)]
pub enum Commands {
    /// Generate shell completion scripts for bestls.
    ///
    /// This subcommand generates completion scripts for various shells,
    /// enabling tab completion for commands, options, and arguments.
    Completion {
        /// The target shell to generate completions for.
        ///
        /// Supported shells include bash, zsh, fish, powershell, and elvish.
        /// The generated script should be saved to the appropriate location
        /// for your shell's completion system.
        #[arg(value_enum)]
        shell: Shell,
    },

    /// Manage bestls theme and configuration.
    ///
    /// This subcommand helps users manage color themes and configuration.
    Theme {
        #[command(subcommand)]
        subcommand: ThemeSubcommand,
    },
}

#[derive(Debug, Parser)]
pub enum ThemeSubcommand {
    /// Initialize a sample config file at ~/.config/bestls/config.toml
    Init {
        /// Show the config file path after creation
        #[arg(long)]
        show: bool,
    },

    /// Show the path to the config file
    Path,

    /// Reset theme to default
    Reset,
}

impl Cli {
    /// Compute the effective output format, honoring legacy flags.
    ///
    /// This method resolves the output format by checking legacy flags (`--json`, `--json-pretty`)
    /// first for backward compatibility, and falling back to the `--format` option if no legacy flags
    /// are set. This centralizes the logic for handling the "two knobs for one concept" issue.
    ///
    /// # Returns
    ///
    /// The effective `OutputFormat` to use for output rendering.
    ///
    /// # Behavior
    ///
    /// - If `--json-pretty` is set, returns `OutputFormat::JsonPretty`
    /// - Else if `--json` is set, returns `OutputFormat::Json`
    /// - Otherwise returns the value of `--format`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use bestls::cli::Cli;
    /// use clap::Parser;
    ///
    /// // Example with --json flag
    /// // CLI args: bestls --json
    /// // Result: OutputFormat::Json (legacy flag takes precedence)
    ///
    /// // Example with --format option
    /// // CLI args: bestls --format json-pretty
    /// // Result: OutputFormat::JsonPretty
    ///
    /// // Example with both (legacy takes precedence)
    /// // CLI args: bestls --format table --json
    /// // Result: OutputFormat::Json (legacy flag overrides)
    /// ```
    pub fn effective_format(&self) -> OutputFormat {
        // Legacy flags override `--format` for compatibility
        if self.json_pretty {
            OutputFormat::JsonPretty
        } else if self.json {
            OutputFormat::Json
        } else {
            self.format
        }
    }

    /// Generate and output shell completion scripts to stdout.
    ///
    /// This method creates completion scripts for the specified shell using `clap_complete`.
    /// The generated script provides tab completion for all commands, options, and their values.
    ///
    /// # Arguments
    ///
    /// * `shell` - The target shell type (bash, zsh, fish, etc.)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use bestls::cli::Cli;
    /// use clap_complete::Shell;
    ///
    /// // Generate bash completions
    /// Cli::generate_completion(Shell::Bash);
    /// ```
    ///
    /// # Usage
    ///
    /// Typically called from the main function when the completion subcommand is used:
    ///
    /// ```bash
    /// # Generate and save bash completions
    /// bestls completion bash > ~/.local/share/bash-completion/completions/bestls
    /// ```
    ///
    /// # Output
    ///
    /// The completion script is written to stdout, allowing for easy redirection
    /// to the appropriate completion directory for your shell.
    pub fn generate_completion(shell: Shell) {
        let mut cmd = Self::command();
        let name = cmd.get_name().to_string();
        generate(shell, &mut cmd, name, &mut io::stdout());
    }
}