#![cfg_attr(coverage_nightly, coverage(off))]
use crate::ast::polyglot::Language;
use crate::utils::path_validator::PathValidator;
use anyhow::{anyhow, Result};
use std::path::{Path, PathBuf};
pub struct PolyglotPathValidator;
impl PolyglotPathValidator {
pub fn validate_directory_path(path: &Path) -> Result<()> {
PathValidator::validate_directory_anyhow(path)
.map_err(|e| anyhow!("Invalid polyglot directory path: {}", e))
}
pub fn validate_file_path(path: &Path) -> Result<()> {
PathValidator::validate_file_anyhow(path)
.map_err(|e| anyhow!("Invalid polyglot file path: {}", e))
}
pub fn is_valid_language_file(path: &Path, language: Option<Language>) -> bool {
if !PathValidator::is_valid_file(path) {
return false;
}
match language {
Some(lang) => Self::is_file_for_language(path, lang),
None => Self::is_any_supported_language_file(path),
}
}
pub fn is_file_for_language(path: &Path, language: Language) -> bool {
path.extension()
.and_then(|ext| ext.to_str())
.map(|ext| {
language
.file_extensions()
.iter()
.any(|&lang_ext| lang_ext.eq_ignore_ascii_case(ext))
})
.unwrap_or(false)
}
pub fn is_any_supported_language_file(path: &Path) -> bool {
Language::from_path(path).is_some()
}
pub fn get_language_files_in_dir(
directory: &Path,
language: Language,
recursive: bool,
) -> Result<Vec<PathBuf>> {
Self::validate_directory_path(directory)?;
let mut result = Vec::new();
Self::collect_language_files_in_dir(directory, language, recursive, &mut result)?;
Ok(result)
}
fn collect_language_files_in_dir(
directory: &Path,
language: Language,
recursive: bool,
results: &mut Vec<PathBuf>,
) -> Result<()> {
if !directory.is_dir() {
return Ok(());
}
for entry in std::fs::read_dir(directory)? {
let entry = entry?;
let path = entry.path();
if path.is_file() && Self::is_file_for_language(&path, language) {
results.push(path.clone());
} else if recursive && path.is_dir() {
Self::collect_language_files_in_dir(&path, language, recursive, results)?;
}
}
Ok(())
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_validate_directory_path() -> Result<()> {
let temp_dir = TempDir::new()?;
assert!(PolyglotPathValidator::validate_directory_path(temp_dir.path()).is_ok());
let non_existent = temp_dir.path().join("non_existent");
assert!(PolyglotPathValidator::validate_directory_path(&non_existent).is_err());
Ok(())
}
#[test]
fn test_validate_file_path() -> Result<()> {
let temp_dir = TempDir::new()?;
let file_path = temp_dir.path().join("test.java");
fs::write(&file_path, "public class Test {}")?;
assert!(PolyglotPathValidator::validate_file_path(&file_path).is_ok());
let non_existent = temp_dir.path().join("non_existent.java");
assert!(PolyglotPathValidator::validate_file_path(&non_existent).is_err());
Ok(())
}
#[test]
fn test_is_valid_language_file() -> Result<()> {
let temp_dir = TempDir::new()?;
let java_file = temp_dir.path().join("Test.java");
let kotlin_file = temp_dir.path().join("Test.kt");
let txt_file = temp_dir.path().join("readme.txt");
fs::write(&java_file, "public class Test {}")?;
fs::write(&kotlin_file, "data class Test(val name: String)")?;
fs::write(&txt_file, "This is a text file")?;
assert!(PolyglotPathValidator::is_valid_language_file(
&java_file,
Some(Language::Java)
));
assert!(!PolyglotPathValidator::is_valid_language_file(
&java_file,
Some(Language::Kotlin)
));
assert!(PolyglotPathValidator::is_valid_language_file(
&kotlin_file,
Some(Language::Kotlin)
));
assert!(!PolyglotPathValidator::is_valid_language_file(
&txt_file,
Some(Language::Java)
));
assert!(PolyglotPathValidator::is_valid_language_file(
&java_file, None
));
assert!(PolyglotPathValidator::is_valid_language_file(
&kotlin_file,
None
));
assert!(!PolyglotPathValidator::is_valid_language_file(
&txt_file, None
));
let non_existent = temp_dir.path().join("non_existent.java");
assert!(!PolyglotPathValidator::is_valid_language_file(
&non_existent,
Some(Language::Java)
));
Ok(())
}
#[test]
fn test_is_file_for_language() {
assert!(PolyglotPathValidator::is_file_for_language(
Path::new("test.java"),
Language::Java
));
assert!(PolyglotPathValidator::is_file_for_language(
Path::new("test.kt"),
Language::Kotlin
));
assert!(!PolyglotPathValidator::is_file_for_language(
Path::new("test.java"),
Language::Kotlin
));
assert!(!PolyglotPathValidator::is_file_for_language(
Path::new("test.txt"),
Language::Java
));
}
#[test]
fn test_get_language_files_in_dir() -> Result<()> {
let temp_dir = TempDir::new()?;
let nested_dir = temp_dir.path().join("nested");
fs::create_dir(&nested_dir)?;
let java_file1 = temp_dir.path().join("Test1.java");
let java_file2 = temp_dir.path().join("Test2.java");
let java_file3 = nested_dir.join("Test3.java");
let kotlin_file = temp_dir.path().join("Test.kt");
fs::write(&java_file1, "public class Test1 {}")?;
fs::write(&java_file2, "public class Test2 {}")?;
fs::write(&java_file3, "public class Test3 {}")?;
fs::write(&kotlin_file, "data class Test(val name: String)")?;
let java_files = PolyglotPathValidator::get_language_files_in_dir(
temp_dir.path(),
Language::Java,
false,
)?;
assert_eq!(java_files.len(), 2);
assert!(java_files.contains(&java_file1));
assert!(java_files.contains(&java_file2));
assert!(!java_files.contains(&java_file3));
let java_files_recursive = PolyglotPathValidator::get_language_files_in_dir(
temp_dir.path(),
Language::Java,
true,
)?;
assert_eq!(java_files_recursive.len(), 3);
assert!(java_files_recursive.contains(&java_file1));
assert!(java_files_recursive.contains(&java_file2));
assert!(java_files_recursive.contains(&java_file3));
let kotlin_files = PolyglotPathValidator::get_language_files_in_dir(
temp_dir.path(),
Language::Kotlin,
true,
)?;
assert_eq!(kotlin_files.len(), 1);
assert!(kotlin_files.contains(&kotlin_file));
Ok(())
}
}