shadow_crypt_shell/encryption/
validation.rs1use std::path::PathBuf;
2
3use crate::{
4 encryption::{cli::CliArgs, file::EncryptionInputFile},
5 errors::{WorkflowError, WorkflowResult},
6};
7
8pub struct ValidEncryptionArgs {
9 pub files: Vec<EncryptionInputFile>,
10 pub test_mode: bool,
11}
12
13pub fn validate_input(input: CliArgs) -> WorkflowResult<ValidEncryptionArgs> {
14 ensure_not_empty(&input)?;
15
16 let validated_files: Vec<EncryptionInputFile> = input
17 .input_files
18 .iter()
19 .map(PathBuf::from)
20 .map(ensure_exists)
21 .map(ensure_is_regular_file)
22 .map(create_input_file)
23 .collect::<WorkflowResult<Vec<EncryptionInputFile>>>()?;
24
25 Ok(ValidEncryptionArgs {
26 files: validated_files,
27 test_mode: input.test_mode,
28 })
29}
30
31fn ensure_not_empty(input: &CliArgs) -> WorkflowResult<()> {
32 if input.input_files.is_empty() {
33 return Err(WorkflowError::UserInput(
34 "No input files provided".to_string(),
35 ));
36 }
37 Ok(())
38}
39
40fn ensure_exists(path: PathBuf) -> WorkflowResult<PathBuf> {
41 if !path.exists() {
42 return Err(WorkflowError::UserInput(format!(
43 "Input file does not exist: {}",
44 path.display()
45 )));
46 }
47 Ok(path)
48}
49
50fn ensure_is_regular_file(path: WorkflowResult<PathBuf>) -> WorkflowResult<PathBuf> {
51 if let Ok(path) = &path
52 && !path.is_file()
53 {
54 return Err(WorkflowError::UserInput(format!(
55 "Input path is not a file: {}",
56 path.display()
57 )));
58 }
59 path
60}
61
62fn create_input_file(path: WorkflowResult<PathBuf>) -> WorkflowResult<EncryptionInputFile> {
63 let path = path?;
64 let name: String = path
65 .file_name()
66 .and_then(|n| n.to_str())
67 .ok_or_else(|| {
68 WorkflowError::UserInput(format!("Invalid filename for path: {}", path.display()))
69 })?
70 .to_string();
71 let size: u64 = path
72 .metadata()
73 .map_err(|_| {
74 WorkflowError::UserInput(format!(
75 "Unable to read metadata for file: {}",
76 path.display()
77 ))
78 })?
79 .len();
80
81 Ok(EncryptionInputFile {
82 path,
83 filename: name,
84 size,
85 })
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use std::io::Write;
92 use tempfile::NamedTempFile;
93
94 #[test]
95 fn test_validate_input_no_files() {
96 let input = CliArgs {
97 input_files: vec![],
98 test_mode: false,
99 };
100 let result = validate_input(input);
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
109 #[test]
110 fn test_validate_input_file_does_not_exist() {
111 let input = CliArgs {
112 input_files: vec!["nonexistent_file.txt".to_string()],
113 test_mode: false,
114 };
115 let result = validate_input(input);
116 assert!(result.is_err());
117 if let Err(WorkflowError::UserInput(msg)) = result {
118 assert!(msg.contains("Input file does not exist"));
119 } else {
120 panic!("Expected UserInput error");
121 }
122 }
123
124 #[test]
125 fn test_validate_input_path_is_directory() {
126 let temp_dir = tempfile::tempdir().unwrap();
127 let input = CliArgs {
128 input_files: vec![temp_dir.path().to_str().unwrap().to_string()],
129 test_mode: false,
130 };
131 let result = validate_input(input);
132 assert!(result.is_err());
133 if let Err(WorkflowError::UserInput(msg)) = result {
134 assert!(msg.contains("Input path is not a file"));
135 } else {
136 panic!("Expected UserInput error");
137 }
138 }
139
140 #[test]
141 fn test_validate_input_valid_file() {
142 let mut temp_file = NamedTempFile::new().unwrap();
143 let content = b"Hello, world!";
144 temp_file.write_all(content).unwrap();
145 let file_path = temp_file.path().to_path_buf();
146
147 let input = CliArgs {
148 input_files: vec![file_path.to_str().unwrap().to_string()],
149 test_mode: true,
150 };
151 let result = validate_input(input);
152 assert!(result.is_ok());
153 let valid_args = result.unwrap();
154 assert_eq!(valid_args.files.len(), 1);
155 assert!(valid_args.test_mode);
156 let file = &valid_args.files[0];
157 assert_eq!(file.path, file_path);
158 assert_eq!(
159 file.filename,
160 file_path.file_name().unwrap().to_str().unwrap()
161 );
162 assert_eq!(file.size, content.len() as u64);
163 }
164
165 #[test]
166 fn test_validate_input_multiple_files() {
167 let mut temp_file1 = NamedTempFile::new().unwrap();
168 temp_file1.write_all(b"File 1").unwrap();
169 let path1 = temp_file1.path().to_path_buf();
170
171 let mut temp_file2 = NamedTempFile::new().unwrap();
172 temp_file2.write_all(b"File 2 content").unwrap();
173 let path2 = temp_file2.path().to_path_buf();
174
175 let input = CliArgs {
176 input_files: vec![
177 path1.to_str().unwrap().to_string(),
178 path2.to_str().unwrap().to_string(),
179 ],
180 test_mode: false,
181 };
182 let result = validate_input(input);
183 assert!(result.is_ok());
184 let valid_args = result.unwrap();
185 assert_eq!(valid_args.files.len(), 2);
186 assert!(!valid_args.test_mode);
187
188 let file1 = &valid_args.files[0];
190 assert_eq!(file1.path, path1);
191 assert_eq!(file1.filename, path1.file_name().unwrap().to_str().unwrap());
192 assert_eq!(file1.size, 6); let file2 = &valid_args.files[1];
196 assert_eq!(file2.path, path2);
197 assert_eq!(file2.filename, path2.file_name().unwrap().to_str().unwrap());
198 assert_eq!(file2.size, 14); }
200}