use std::path::PathBuf;
use shadow_crypt_core::profile::SecurityProfile;
use crate::{
encryption::{
cli::EncryptionCliArgs,
file::{EncryptionInputFile, InputKind},
},
errors::{WorkflowError, WorkflowResult},
};
pub struct ValidEncryptionArgs {
pub files: Vec<EncryptionInputFile>,
pub security_profile: SecurityProfile,
pub output_dir: Option<PathBuf>,
pub password_file: Option<PathBuf>,
pub quiet: bool,
pub delete: bool,
}
pub fn validate_input(input: EncryptionCliArgs) -> WorkflowResult<ValidEncryptionArgs> {
if input.input_files.is_empty() {
return Err(WorkflowError::UserInput(
"No input files provided".to_string(),
));
}
let mut validated_files: Vec<EncryptionInputFile> = Vec::new();
for raw in &input.input_files {
validate_path(PathBuf::from(raw), &mut validated_files)?;
}
Ok(ValidEncryptionArgs {
files: validated_files,
security_profile: input.profile.into(),
output_dir: input.output_dir,
password_file: input.password_file,
quiet: input.quiet,
delete: input.delete,
})
}
fn validate_path(path: PathBuf, out: &mut Vec<EncryptionInputFile>) -> WorkflowResult<()> {
if !path.exists() {
return Err(WorkflowError::UserInput(format!(
"Input path does not exist: {}",
path.display()
)));
}
let name = path_name(&path)?;
if path.is_dir() {
out.push(EncryptionInputFile {
path,
filename: name,
size: 0,
kind: InputKind::Directory,
});
return Ok(());
}
if !path.is_file() {
return Err(WorkflowError::UserInput(format!(
"Input path is not a file or directory: {}",
path.display()
)));
}
let size: u64 = path
.metadata()
.map_err(|_| {
WorkflowError::UserInput(format!(
"Unable to read metadata for file: {}",
path.display()
))
})?
.len();
out.push(EncryptionInputFile {
path,
filename: name,
size,
kind: InputKind::File,
});
Ok(())
}
fn path_name(path: &std::path::Path) -> WorkflowResult<String> {
Ok(path
.file_name()
.and_then(|n| n.to_str())
.ok_or_else(|| {
WorkflowError::UserInput(format!("Invalid filename for path: {}", path.display()))
})?
.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_validate_input_no_files() {
let input = EncryptionCliArgs {
input_files: vec![],
..Default::default()
};
let result = validate_input(input);
assert!(result.is_err());
if let Err(WorkflowError::UserInput(msg)) = result {
assert_eq!(msg, "No input files provided");
} else {
panic!("Expected UserInput error");
}
}
#[test]
fn test_validate_input_file_does_not_exist() {
let input = EncryptionCliArgs {
input_files: vec!["nonexistent_file.txt".to_string()],
..Default::default()
};
let result = validate_input(input);
assert!(result.is_err());
if let Err(WorkflowError::UserInput(msg)) = result {
assert!(msg.contains("Input path does not exist"));
} else {
panic!("Expected UserInput error");
}
}
#[test]
fn test_validate_input_directory_becomes_archive_item() {
let temp_dir = tempfile::tempdir().unwrap();
let dir = temp_dir.path().join("photos");
std::fs::create_dir(&dir).unwrap();
std::fs::write(dir.join("a.txt"), b"a").unwrap();
let input = EncryptionCliArgs {
input_files: vec![dir.to_str().unwrap().to_string()],
..Default::default()
};
let valid = validate_input(input).unwrap();
assert_eq!(valid.files.len(), 1);
assert_eq!(valid.files[0].kind, InputKind::Directory);
assert_eq!(valid.files[0].filename, "photos");
}
#[test]
fn test_validate_input_valid_file() {
let mut temp_file = NamedTempFile::new().unwrap();
let content = b"Hello, world!";
temp_file.write_all(content).unwrap();
let file_path = temp_file.path().to_path_buf();
let input = EncryptionCliArgs {
input_files: vec![file_path.to_str().unwrap().to_string()],
profile: crate::encryption::cli::CliProfile::Test,
..Default::default()
};
let result = validate_input(input);
assert!(result.is_ok());
let valid_args = result.unwrap();
assert_eq!(valid_args.files.len(), 1);
assert_eq!(valid_args.security_profile, SecurityProfile::Test);
let file = &valid_args.files[0];
assert_eq!(file.path, file_path);
assert_eq!(
file.filename,
file_path.file_name().unwrap().to_str().unwrap()
);
assert_eq!(file.size, content.len() as u64);
}
#[test]
fn test_validate_input_multiple_files() {
let mut temp_file1 = NamedTempFile::new().unwrap();
temp_file1.write_all(b"File 1").unwrap();
let path1 = temp_file1.path().to_path_buf();
let mut temp_file2 = NamedTempFile::new().unwrap();
temp_file2.write_all(b"File 2 content").unwrap();
let path2 = temp_file2.path().to_path_buf();
let input = EncryptionCliArgs {
input_files: vec![
path1.to_str().unwrap().to_string(),
path2.to_str().unwrap().to_string(),
],
..Default::default()
};
let result = validate_input(input);
assert!(result.is_ok());
let valid_args = result.unwrap();
assert_eq!(valid_args.files.len(), 2);
assert_eq!(valid_args.security_profile, SecurityProfile::Standard);
let file1 = &valid_args.files[0];
assert_eq!(file1.path, path1);
assert_eq!(file1.filename, path1.file_name().unwrap().to_str().unwrap());
assert_eq!(file1.size, 6);
let file2 = &valid_args.files[1];
assert_eq!(file2.path, path2);
assert_eq!(file2.filename, path2.file_name().unwrap().to_str().unwrap());
assert_eq!(file2.size, 14); }
}