use crate::error::{Error, Result};
use crate::model::Project;
use crate::parser::{GoldFile, GoldParser};
use std::path::{Path, PathBuf};
pub struct ProjectLoader {
root: PathBuf,
}
impl ProjectLoader {
pub fn new<P: AsRef<Path>>(root: P) -> Self {
Self {
root: root.as_ref().to_path_buf(),
}
}
pub fn load(&self) -> Result<Project> {
let gold_files = self.discover_gold_files()?;
if gold_files.is_empty() {
return Err(Error::Config {
message: format!(
"No gold files (.au) found in directory: {}",
self.root.display()
),
});
}
let parsed_files = self.parse_gold_files(&gold_files)?;
Project::from_gold_files(parsed_files)
}
fn discover_gold_files(&self) -> Result<Vec<PathBuf>> {
let mut gold_files = Vec::new();
self.discover_recursive(&self.root, &mut gold_files)?;
gold_files.sort();
Ok(gold_files)
}
fn discover_recursive(&self, dir: &Path, files: &mut Vec<PathBuf>) -> Result<()> {
if !dir.exists() {
return Err(Error::Config {
message: format!("Directory does not exist: {}", dir.display()),
});
}
if !dir.is_dir() {
return Err(Error::Config {
message: format!("Path is not a directory: {}", dir.display()),
});
}
let entries = std::fs::read_dir(dir).map_err(Error::Io)?;
for entry in entries {
let entry = entry.map_err(Error::Io)?;
let path = entry.path();
if path.is_dir() {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if name.starts_with('.') || name == "target" || name == "build" {
continue;
}
}
self.discover_recursive(&path, files)?;
} else if path.extension().and_then(|e| e.to_str()) == Some("au") {
files.push(path);
}
}
Ok(())
}
fn parse_gold_files(&self, paths: &[PathBuf]) -> Result<Vec<GoldFile>> {
let mut parsed = Vec::new();
for path in paths {
match GoldParser::parse_file(path) {
Ok(file) => parsed.push(file),
Err(e) => {
return Err(Error::Parse {
file: path.to_path_buf(),
line: 0,
column: 0,
message: format!("Failed to parse gold file: {}", e),
});
}
}
}
Ok(parsed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_loader_creation() {
let loader = ProjectLoader::new("./test");
assert_eq!(loader.root, PathBuf::from("./test"));
}
#[test]
fn test_discover_nonexistent_directory() {
let loader = ProjectLoader::new("/nonexistent/path/that/does/not/exist");
let result = loader.discover_gold_files();
assert!(result.is_err());
}
}