Skip to main content

csh/
args.rs

1//! Command-line argument parsing
2//!
3//! Parses CLI arguments using the clap library and provides structured access
4//! to user-provided options.
5
6use clap::{Arg, Command};
7
8/// Parsed command-line arguments
9#[derive(Debug, Clone)]
10pub struct MainArgs {
11    /// Enable debug logging to file
12    pub debug: bool,
13    /// Enable SSH session logging to file
14    pub ssh_logging: bool,
15    /// Arguments to pass through to the SSH command
16    pub ssh_args: Vec<String>,
17    /// Argument to pass for configuration profiles
18    pub profile: Option<String>,
19    /// Whether the SSH command is non-interactive (e.g., -G, -V, -O, -Q, -T)
20    pub is_non_interactive: bool,
21}
22
23/// Parses command-line arguments using clap.
24///
25/// # Arguments Supported
26/// - `-d, --debug` - Enable debug mode with detailed logging
27/// - `-l, --log` - Enable SSH session logging
28/// - `ssh_args` - All remaining arguments are passed to SSH
29///
30/// # Examples
31/// ```text
32/// csh -d user@example.com          # Debug mode enabled
33/// csh -l user@example.com          # SSH logging enabled
34/// csh -d -l user@example.com -p 22 # Both modes with SSH args
35/// csh -- -G user@example.com       # Non-interactive command (config dump).
36/// ```
37///
38/// # Returns
39/// A MainArgs struct containing all parsed arguments
40pub fn main_args() -> MainArgs {
41    let matches = Command::new("csh")
42        .version("v0.5.1")
43        .author("@karsyboy")
44        .about("A Rust-based SSH client wrapper with syntax highlighting and logging capabilities")
45        .arg_required_else_help(true)
46        .propagate_version(true)
47        .arg(
48            Arg::new("debug")
49                .short('d')
50                .long("debug")
51                .help("Enable debug mode with detailed logging to ~/.csh/logs/csh.log")
52                .action(clap::ArgAction::SetTrue),
53        )
54        .arg(
55            Arg::new("log")
56                .short('l')
57                .long("log")
58                .help("Enable SSH session logging to ~/.csh/logs/ssh_sessions/")
59                .action(clap::ArgAction::SetTrue),
60        )
61        .arg(
62            Arg::new("profile")
63                .short('P')
64                .long("profile")
65                .help("Specify a configuration profile to use")
66                .num_args(1)
67                .required(false)
68                .default_value(""),
69        )
70        .arg(
71            Arg::new("ssh_args")
72                .help("SSH arguments to forward to the SSH command")
73                .num_args(1..)
74                .required(true),
75        )
76        .after_help(
77            r#"
78csh -d user@example.com                          # Debug mode enabled
79csh -l user@example.com                          # SSH logging enabled
80csh -l -P network user@firewall.example.com      # Use 'network' config profile
81csh -l user@host -p 2222 -i ~/.ssh/custom_key    # Both modes with SSH args
82csh user@host -G                                 # Non-interactive command
83"#,
84        )
85        .get_matches();
86
87    // Retrieve SSH arguments to forward
88    let ssh_args: Vec<String> = matches.get_many::<String>("ssh_args").map(|vals| vals.cloned().collect()).unwrap_or_default();
89
90    // Detect non-interactive SSH commands that don't need highlighting
91    // These commands typically output configuration or version info
92    let is_non_interactive = ssh_args.iter().any(|arg| matches!(arg.as_str(), "-G" | "-V" | "-O" | "-Q" | "-T"));
93
94    MainArgs {
95        debug: matches.get_flag("debug"),
96        ssh_logging: matches.get_flag("log"),
97        profile: matches.get_one::<String>("profile").cloned().filter(|s| !s.is_empty()),
98        ssh_args,
99        is_non_interactive,
100    }
101}