1use std::path::Path;
2
3#[derive(Debug, PartialEq)]
4pub enum Op { Enc, Dec }
5
6#[derive(Debug)]
7pub struct Args {
8 pub op: Op,
9 pub input_path: String,
10 pub output_path: String,
11 pub password: Option<String>,
12 pub quiet: bool,
13}
14pub fn parse_args(args: &Vec<String>) -> Result<Args, String> {
15 if args.len() < 2 {
17 return Err("arg too short".to_string());
18 }
19
20 let op = {
22 match args[0].as_str() {
23 "-e" | "--encrypt" => { Op::Enc }
24 "-d" | "--decrypt" => { Op::Dec }
25 _ => {
26 return Err("unknown operation".to_string())
27 }
28 }
29 };
30
31 let input_path = args[1].clone();
33
34 if !Path::new(&input_path).exists() {
36 return Err("no such file".to_string())
37 }
38
39 let mut quiet = false;
40 let mut output_path: Option<String> = None;
41 let mut password: Option<String> = None;
42
43 if args.len() > 2 {
44 let mut skip = false;
45 let mut i: usize = 2;
46 for v in &args[2..] {
47 i += 1;
48 if skip { skip = false; continue; }
49 match v.as_str() {
50 "-q" | "--quiet" => { quiet = true; }
51
52 "-p" | "--password" => {
53 if password == None {
54 password = Some(args[i].clone());
55 skip = true;
56 } else {
57 return Err("one password option only".to_string());
58 }
59 }
60
61 "-o" | "--output" => {
62 if output_path == None {
63 output_path = Some(args[i].clone());
64 skip = true;
65 } else {
66 return Err("one output option only".to_string());
67 }
68 }
69
70 _ => {
71 return Err("unknown option".to_string());
72 }
73 }
74 }
75 }
76
77 if output_path == None {
79 match op {
80 Op::Enc => output_path = Some(format!("{}.decx", input_path)),
81 Op::Dec => {
82 if input_path.ends_with(".decx") {
83 output_path = Some(input_path[..input_path.len() - 5].to_string());
84 } else {
85 output_path = Some(format!("{}.out", input_path));
86 }
87 }
88 }
89 }
90
91 let output = match output_path {
92 Some(s) => s,
93 _ => unreachable!()
94 };
95
96 Ok(Args { op, input_path, output_path: output, password, quiet })
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_parse_args_encrypt_basic() {
105 let test_file = create_test_file("test_input.txt");
107
108 let args = vec![
109 "-e".to_string(),
110 test_file.path().to_str().unwrap().to_string()
111 ];
112
113 let result = parse_args(&args);
114 assert!(result.is_ok());
115 let parsed_args = result.unwrap();
116 assert_eq!(parsed_args.op, Op::Enc);
117 assert_eq!(parsed_args.output_path, format!("{}.decx", test_file.path().to_str().unwrap()));
118 assert_eq!(parsed_args.quiet, false);
119 }
120
121 #[test]
122 fn test_parse_args_decrypt_with_options() {
123 let test_file = create_test_file("test_input.decx");
125
126 let args = vec![
127 "-d".to_string(),
128 test_file.path().to_str().unwrap().to_string(),
129 "-o".to_string(),
130 "custom_output.txt".to_string(),
131 "-p".to_string(),
132 "testpassword".to_string(),
133 "-q".to_string()
134 ];
135
136 let result = parse_args(&args);
137 assert!(result.is_ok());
138 let parsed_args = result.unwrap();
139 assert_eq!(parsed_args.op, Op::Dec);
140 assert_eq!(parsed_args.output_path, "custom_output.txt");
141 assert_eq!(parsed_args.password, Some("testpassword".to_string()));
142 assert_eq!(parsed_args.quiet, true);
143 }
144
145 #[test]
146 fn test_parse_args_invalid_operation() {
147 let args = vec!["-x".to_string(), "input.txt".to_string()];
148 let result = parse_args(&args);
149 assert!(result.is_err());
150 assert_eq!(result.unwrap_err(), "unknown operation");
151 }
152
153 #[test]
154 fn test_parse_args_missing_arguments() {
155 let args = vec!["-e".to_string()];
156 let result = parse_args(&args);
157 assert!(result.is_err());
158 assert_eq!(result.unwrap_err(), "arg too short");
159 }
160
161 #[test]
162 fn test_parse_args_file_not_found() {
163 let args = vec!["-e".to_string(), "nonexistent.txt".to_string()];
164 let result = parse_args(&args);
165 assert!(result.is_err());
166 assert_eq!(result.unwrap_err(), "no such file");
167 }
168
169 fn create_test_file(_name: &str) -> tempfile::NamedTempFile {
171 let file = tempfile::NamedTempFile::new().unwrap();
172 std::fs::write(file.path(), "test content").unwrap();
173 file
174 }
175}