use crate::config::Config;
use crate::error::StorageResult;
use std::path::Path;
pub trait IndustryFileAdapter: Send + Sync {
fn name(&self) -> &'static str;
fn can_handle(&self, project_root: &Path) -> bool;
fn read_config(&self, project_root: &Path) -> StorageResult<Config>;
fn priority(&self) -> u32 {
0
}
}
#[derive(Debug, Clone)]
pub struct FileDetectionResult {
pub adapter_name: String,
pub priority: u32,
pub file_path: std::path::PathBuf,
}
pub struct IndustryFileDetector {
adapters: Vec<Box<dyn IndustryFileAdapter>>,
}
impl IndustryFileDetector {
pub fn new(adapters: Vec<Box<dyn IndustryFileAdapter>>) -> Self {
IndustryFileDetector { adapters }
}
pub fn detect_files(&self, project_root: &Path) -> Vec<FileDetectionResult> {
let mut results = Vec::new();
for adapter in &self.adapters {
if adapter.can_handle(project_root) {
results.push(FileDetectionResult {
adapter_name: adapter.name().to_string(),
priority: adapter.priority(),
file_path: project_root.to_path_buf(),
});
}
}
results.sort_by(|a, b| b.priority.cmp(&a.priority));
results
}
pub fn get_best_adapter(&self, project_root: &Path) -> Option<&dyn IndustryFileAdapter> {
self.adapters
.iter()
.filter(|adapter| adapter.can_handle(project_root))
.max_by_key(|adapter| adapter.priority())
.map(|adapter| adapter.as_ref())
}
pub fn register_adapter(&mut self, adapter: Box<dyn IndustryFileAdapter>) {
self.adapters.push(adapter);
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockAdapter {
name: &'static str,
priority: u32,
can_handle: bool,
}
impl IndustryFileAdapter for MockAdapter {
fn name(&self) -> &'static str {
self.name
}
fn can_handle(&self, _project_root: &Path) -> bool {
self.can_handle
}
fn read_config(&self, _project_root: &Path) -> StorageResult<Config> {
Ok(Config::default())
}
fn priority(&self) -> u32 {
self.priority
}
}
#[test]
fn test_detector_sorts_by_priority() {
let adapters: Vec<Box<dyn IndustryFileAdapter>> = vec![
Box::new(MockAdapter {
name: "low",
priority: 1,
can_handle: true,
}),
Box::new(MockAdapter {
name: "high",
priority: 10,
can_handle: true,
}),
Box::new(MockAdapter {
name: "medium",
priority: 5,
can_handle: true,
}),
];
let detector = IndustryFileDetector::new(adapters);
let results = detector.detect_files(Path::new("."));
assert_eq!(results.len(), 3);
assert_eq!(results[0].adapter_name, "high");
assert_eq!(results[1].adapter_name, "medium");
assert_eq!(results[2].adapter_name, "low");
}
#[test]
fn test_detector_filters_by_can_handle() {
let adapters: Vec<Box<dyn IndustryFileAdapter>> = vec![
Box::new(MockAdapter {
name: "yes",
priority: 1,
can_handle: true,
}),
Box::new(MockAdapter {
name: "no",
priority: 10,
can_handle: false,
}),
];
let detector = IndustryFileDetector::new(adapters);
let results = detector.detect_files(Path::new("."));
assert_eq!(results.len(), 1);
assert_eq!(results[0].adapter_name, "yes");
}
#[test]
fn test_get_best_adapter() {
let adapters: Vec<Box<dyn IndustryFileAdapter>> = vec![
Box::new(MockAdapter {
name: "low",
priority: 1,
can_handle: true,
}),
Box::new(MockAdapter {
name: "high",
priority: 10,
can_handle: true,
}),
];
let detector = IndustryFileDetector::new(adapters);
let best = detector.get_best_adapter(Path::new("."));
assert!(best.is_some());
assert_eq!(best.unwrap().name(), "high");
}
#[test]
fn test_get_best_adapter_respects_can_handle() {
let adapters: Vec<Box<dyn IndustryFileAdapter>> = vec![
Box::new(MockAdapter {
name: "low",
priority: 1,
can_handle: true,
}),
Box::new(MockAdapter {
name: "high",
priority: 10,
can_handle: false,
}),
];
let detector = IndustryFileDetector::new(adapters);
let best = detector.get_best_adapter(Path::new("."));
assert!(best.is_some());
assert_eq!(best.unwrap().name(), "low");
}
}