use crate::error::Error;
use std::path::Path;
pub fn validate_and_split_paths(paths_str: &str) -> Result<Vec<String>, Error> {
if paths_str.trim().is_empty() {
return Err(Error::invalid_path(paths_str, "path cannot be empty"));
}
let file_paths: Vec<String> = paths_str
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
if file_paths.is_empty() {
return Err(Error::invalid_path(paths_str, "no valid file paths found"));
}
for path in &file_paths {
if path.contains("..") {
return Err(Error::invalid_path(path, "path traversal not allowed"));
}
if path.len() > 260 {
return Err(Error::invalid_path(path, "path too long"));
}
if cfg!(windows) {
if path
.chars()
.any(|c| matches!(c, '<' | '>' | '"' | '|' | '?' | '*'))
{
return Err(Error::invalid_path(path, "invalid characters in path"));
}
} else {
if path
.chars()
.any(|c| matches!(c, '<' | '>' | ':' | '"' | '|' | '?' | '*'))
{
return Err(Error::invalid_path(path, "invalid characters in path"));
}
}
}
Ok(file_paths)
}
pub fn check_file_accessibility(path: &str) -> Result<(), Error> {
let path_obj = Path::new(path);
if !path_obj.exists() {
return Err(Error::file_not_found(path));
}
if !path_obj.is_file() {
return Err(Error::invalid_path(path, "path is not a file"));
}
Ok(())
}
pub fn safe_string_to_number<T: std::str::FromStr>(
s: &str,
key: &str,
type_name: &str,
) -> Result<T, Error> {
s.parse()
.map_err(|_| Error::value_conversion_error(key, type_name, s))
}
#[cfg(test)]
mod tests {
use crate::error::Error;
use crate::misc;
#[test]
fn test_validate_and_split_paths_valid() {
let result = misc::validate_and_split_paths("file1.json,file2.json");
assert!(result.is_ok());
assert_eq!(result.unwrap(), vec!["file1.json", "file2.json"]);
}
#[test]
fn test_validate_and_split_paths_empty() {
let result = misc::validate_and_split_paths("");
assert!(result.is_err());
match result.unwrap_err() {
Error::InvalidPathError { .. } => {}
_ => panic!("Expected InvalidPathError"),
}
}
#[test]
fn test_validate_and_split_paths_path_traversal() {
let result = misc::validate_and_split_paths("../config.json");
assert!(result.is_err());
match result.unwrap_err() {
Error::InvalidPathError { .. } => {}
_ => panic!("Expected InvalidPathError"),
}
}
#[test]
fn test_validate_and_split_paths_invalid_chars() {
let result = misc::validate_and_split_paths("config<test>.json");
assert!(result.is_err());
match result.unwrap_err() {
Error::InvalidPathError { .. } => {}
_ => panic!("Expected InvalidPathError"),
}
}
#[test]
fn test_validate_and_split_paths_windows_drive_letter() {
if cfg!(windows) {
let result = misc::validate_and_split_paths(r"C:\config.json");
assert!(result.is_ok());
assert_eq!(result.unwrap(), vec![r"C:\config.json"]);
let result = misc::validate_and_split_paths("C:config.json");
assert!(result.is_ok());
assert_eq!(result.unwrap(), vec!["C:config.json"]);
} else {
let result = misc::validate_and_split_paths("C:config.json");
assert!(result.is_err());
match result.unwrap_err() {
Error::InvalidPathError { .. } => {}
_ => panic!("Expected InvalidPathError"),
}
}
}
#[test]
fn test_check_file_accessibility_nonexistent() {
let result = misc::check_file_accessibility("nonexistent.json");
assert!(result.is_err());
match result.unwrap_err() {
Error::LoadFileError { .. } => {}
_ => panic!("Expected LoadFileError"),
}
}
}