shadow_crypt_shell/encryption/
cli.rs

1use clap::Parser;
2use shadow_crypt_core::profile::SecurityProfile;
3
4use crate::errors::{WorkflowError, WorkflowResult};
5
6/// Encryption CLI arguments structure
7#[derive(Debug, Clone, Parser)]
8#[command(name = "shadow", about = "Encrypt files using shadow format", version)]
9pub struct CliArgs {
10    /// Input files to encrypt
11    #[arg(value_name = "FILE")]
12    pub input_files: Vec<String>,
13
14    /// Use test security profile for faster key derivation (not recommended for production)
15    #[arg(long = "test-mode", short = 't')]
16    pub test_mode: bool,
17}
18
19/// Parse encryption command line arguments
20pub fn get_cli_args(args: Vec<String>) -> WorkflowResult<CliArgs> {
21    let cli_args = CliArgs::try_parse_from(args).map_err(|e| {
22        // If it's help or version, it's not a user input error
23        if e.kind() == clap::error::ErrorKind::DisplayHelp
24            || e.kind() == clap::error::ErrorKind::DisplayVersion
25        {
26            // Print the message and exit successfully
27            eprintln!("{}", e);
28            std::process::exit(0);
29        }
30        WorkflowError::UserInput(e.to_string())
31    })?;
32
33    if cli_args.input_files.is_empty() {
34        return Err(WorkflowError::UserInput(
35            "No input files provided".to_string(),
36        ));
37    }
38
39    Ok(cli_args)
40}
41
42pub fn get_security_profile(test_mode: bool) -> SecurityProfile {
43    if test_mode {
44        SecurityProfile::Test
45    } else {
46        SecurityProfile::Production
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn test_get_security_profile_test_mode() {
56        match get_security_profile(true) {
57            SecurityProfile::Test => {}
58            _ => panic!("Expected Test profile"),
59        }
60    }
61
62    #[test]
63    fn test_get_security_profile_production() {
64        match get_security_profile(false) {
65            SecurityProfile::Production => {}
66            _ => panic!("Expected Production profile"),
67        }
68    }
69
70    #[test]
71    fn test_parse_cli_args_with_files() {
72        let args = vec![
73            "shadow".to_string(),
74            "file1.txt".to_string(),
75            "file2.txt".to_string(),
76        ];
77        let cli_args = get_cli_args(args).unwrap();
78        assert_eq!(
79            cli_args.input_files,
80            vec!["file1.txt".to_string(), "file2.txt".to_string()]
81        );
82        assert!(!cli_args.test_mode);
83    }
84
85    #[test]
86    fn test_parse_cli_args_with_test_mode() {
87        let args = vec![
88            "shadow".to_string(),
89            "--test-mode".to_string(),
90            "file1.txt".to_string(),
91        ];
92        let cli_args = get_cli_args(args).unwrap();
93        assert_eq!(cli_args.input_files, vec!["file1.txt".to_string()]);
94        assert!(cli_args.test_mode);
95    }
96
97    #[test]
98    fn test_parse_cli_args_no_files() {
99        let args = vec!["shadow".to_string()];
100        let result = get_cli_args(args);
101        assert!(result.is_err());
102        if let Err(WorkflowError::UserInput(msg)) = result {
103            assert_eq!(msg, "No input files provided");
104        } else {
105            panic!("Expected UserInput error");
106        }
107    }
108}