use crate::error::{Result, ValidationError};
use std::path::Path;
pub fn validate_file_exists(path: &Path, arg_name: &str) -> Result<()> {
if !path.exists() {
return Err(ValidationError::FileNotFound {
path: path.to_path_buf(),
arg_name: arg_name.to_string(),
suggestion: Some("Check that the file exists and the path is correct.".to_string()),
}
.into());
}
Ok(())
}
pub fn validate_file_extension(path: &Path, arg_name: &str, expected: &[String]) -> Result<()> {
let extension = path
.extension()
.and_then(|ext| ext.to_str())
.map(|ext| ext.to_lowercase());
let ext = match extension {
Some(e) => e,
None => {
return Err(ValidationError::InvalidExtension {
arg_name: arg_name.to_string(),
path: path.to_path_buf(),
expected: expected.to_vec(),
}
.into());
}
};
let is_valid = expected.iter().any(|allowed| allowed.to_lowercase() == ext);
if !is_valid {
return Err(ValidationError::InvalidExtension {
arg_name: arg_name.to_string(),
path: path.to_path_buf(),
expected: expected.to_vec(),
}
.into());
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::io::Write;
use tempfile::TempDir;
fn create_temp_file(dir: &TempDir, name: &str, content: &str) -> std::path::PathBuf {
let file_path = dir.path().join(name);
let mut file = File::create(&file_path).unwrap();
file.write_all(content.as_bytes()).unwrap();
file_path
}
#[test]
fn test_validate_file_exists_valid_file() {
let temp_dir = TempDir::new().unwrap();
let file_path = create_temp_file(&temp_dir, "test.txt", "content");
let result = validate_file_exists(&file_path, "test_file");
assert!(result.is_ok());
}
#[test]
fn test_validate_file_exists_valid_directory() {
let temp_dir = TempDir::new().unwrap();
let result = validate_file_exists(temp_dir.path(), "test_dir");
assert!(result.is_ok());
}
#[test]
fn test_validate_file_exists_nonexistent() {
let temp_dir = TempDir::new().unwrap();
let nonexistent = temp_dir.path().join("does_not_exist.txt");
let result = validate_file_exists(&nonexistent, "missing_file");
assert!(result.is_err());
match result.unwrap_err() {
crate::error::DynamicCliError::Validation(ValidationError::FileNotFound {
path,
arg_name,
..
}) => {
assert_eq!(arg_name, "missing_file");
assert_eq!(path, nonexistent);
}
other => panic!("Expected FileNotFound error, got {:?}", other),
}
}
#[test]
fn test_validate_file_exists_relative_path() {
let temp_dir = TempDir::new().unwrap();
let file_path = create_temp_file(&temp_dir, "relative.txt", "content");
let relative = std::path::Path::new(file_path.file_name().unwrap());
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let result = validate_file_exists(relative, "relative_file");
assert!(result.is_ok());
std::env::set_current_dir(original_dir).unwrap();
}
#[test]
fn test_validate_file_extension_valid_single() {
let path = Path::new("config.yaml");
let allowed = vec!["yaml".to_string()];
let result = validate_file_extension(path, "config", &allowed);
assert!(result.is_ok());
}
#[test]
fn test_validate_file_extension_valid_multiple() {
let path = Path::new("data.csv");
let allowed = vec!["csv".to_string(), "tsv".to_string(), "txt".to_string()];
let result = validate_file_extension(path, "data_file", &allowed);
assert!(result.is_ok());
}
#[test]
fn test_validate_file_extension_case_insensitive() {
let path1 = Path::new("config.YAML");
let allowed = vec!["yaml".to_string()];
assert!(validate_file_extension(path1, "config", &allowed).is_ok());
let path2 = Path::new("config.YaML");
assert!(validate_file_extension(path2, "config", &allowed).is_ok());
let path3 = Path::new("config.yaml");
let allowed_upper = vec!["YAML".to_string()];
assert!(validate_file_extension(path3, "config", &allowed_upper).is_ok());
}
#[test]
fn test_validate_file_extension_invalid() {
let path = Path::new("document.txt");
let allowed = vec!["yaml".to_string(), "yml".to_string()];
let result = validate_file_extension(path, "doc", &allowed);
assert!(result.is_err());
match result.unwrap_err() {
crate::error::DynamicCliError::Validation(ValidationError::InvalidExtension {
arg_name,
path: error_path,
expected,
}) => {
assert_eq!(arg_name, "doc");
assert_eq!(error_path, path);
assert_eq!(expected, allowed);
}
other => panic!("Expected InvalidExtension error, got {:?}", other),
}
}
#[test]
fn test_validate_file_extension_no_extension() {
let path = Path::new("makefile");
let allowed = vec!["txt".to_string()];
let result = validate_file_extension(path, "build_file", &allowed);
assert!(result.is_err());
match result.unwrap_err() {
crate::error::DynamicCliError::Validation(ValidationError::InvalidExtension {
..
}) => {
}
other => panic!("Expected InvalidExtension error, got {:?}", other),
}
}
#[test]
fn test_validate_file_extension_hidden_file_with_extension() {
let path = Path::new(".hidden.txt");
let allowed = vec!["txt".to_string()];
let result = validate_file_extension(path, "hidden_file", &allowed);
assert!(result.is_ok());
}
#[test]
fn test_validate_file_extension_hidden_file_no_extension() {
let path = Path::new(".gitignore");
let allowed = vec!["txt".to_string()];
let result = validate_file_extension(path, "git_file", &allowed);
assert!(result.is_err());
}
#[test]
fn test_validate_file_extension_multiple_dots() {
let path = Path::new("archive.tar.gz");
let allowed = vec!["gz".to_string()];
let result = validate_file_extension(path, "archive", &allowed);
assert!(result.is_ok());
}
#[test]
fn test_validate_file_extension_empty_allowed_list() {
let path = Path::new("file.txt");
let allowed: Vec<String> = vec![];
let result = validate_file_extension(path, "file", &allowed);
assert!(result.is_err());
}
#[test]
fn test_validate_file_extension_with_leading_dot() {
let path = Path::new("config.yaml");
let allowed = vec!["yaml".to_string()];
let result = validate_file_extension(path, "config", &allowed);
assert!(result.is_ok());
}
#[test]
fn test_validate_both_file_and_extension() {
let temp_dir = TempDir::new().unwrap();
let file_path = create_temp_file(&temp_dir, "config.yaml", "key: value");
let result1 = validate_file_exists(&file_path, "config_file");
assert!(result1.is_ok());
let allowed = vec!["yaml".to_string(), "yml".to_string()];
let result2 = validate_file_extension(&file_path, "config_file", &allowed);
assert!(result2.is_ok());
}
#[test]
fn test_validate_wrong_extension_existing_file() {
let temp_dir = TempDir::new().unwrap();
let file_path = create_temp_file(&temp_dir, "data.txt", "some data");
assert!(validate_file_exists(&file_path, "data_file").is_ok());
let allowed = vec!["csv".to_string()];
let result = validate_file_extension(&file_path, "data_file", &allowed);
assert!(result.is_err());
}
#[test]
fn test_validate_extension_nonexistent_file() {
let path = Path::new("nonexistent.yaml");
let allowed = vec!["yaml".to_string()];
let result = validate_file_extension(path, "config", &allowed);
assert!(result.is_ok());
let result2 = validate_file_exists(path, "config");
assert!(result2.is_err());
}
}