fix: resolve CI compilation errors
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-02-05 15:17:51 +00:00
parent 06527a52a4
commit d6c6f16fd4

View File

@@ -0,0 +1,144 @@
use crate::models::{
AnalysisSummary, ByLanguage, ByPriority, CommentType, Config, FileLocation, Priority,
TechDebtItem,
};
mod language;
pub use language::{Language, LanguageParser};
pub mod analyzer;
impl Analyzer {
pub fn new(path: &PathBuf, config_path: &Option<PathBuf>) -> Result<Self> {
let config = load_config(config_path)?;
Ok(Self {
path: path.clone(),
config,
})
}
pub fn analyze(&self) -> Result<Vec<TechDebtItem>> {
let mut items = Vec::new();
let walker = WalkBuilder::new(&self.path)
.hidden(true)
.git_global(true)
.git_ignore(true)
.require_git(false)
.build();
for result in walker {
match result {
Ok(entry) => {
let path = entry.path();
if !self.should_include(path) {
continue;
}
if let Some(language) = Language::from_path(path) {
match self.parse_file(path, &language) {
Ok(mut file_items) => items.append(&mut file_items),
Err(e) => {
eprintln!("Warning: Failed to parse {}: {}", path.display(), e)
}
}
}
}
Err(e) => eprintln!("Warning: Error walking directory: {}", e),
}
}
items.sort_by(|a, b| {
b.priority
.cmp(&a.priority)
.then_with(|| a.location.line.cmp(&b.location.line))
});
Ok(items)
}
fn should_include(&self, path: &PathBuf) -> bool {
if !path.is_file() {
return false;
}
if let Some(ext) = path.extension() {
if let Some(ext_str) = ext.to_str() {
let ext_with_dot = format!(".{}", ext_str);
if !self.config.extensions.contains(&ext_with_dot) {
return false;
}
}
}
for pattern in &self.config.ignore {
if match_ignore_pattern(path, pattern) {
return false;
}
}
true
}
fn parse_file(
&self,
path: &PathBuf,
language: &Language,
) -> Result<Vec<TechDebtItem>> {
let content = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read file: {}", path.display()))?;
let parser = LanguageParser::new(language.clone());
parser.parse(&content, path, &self.config.patterns)
}
}
fn match_ignore_pattern(path: &PathBuf, pattern: &str) -> bool {
if pattern.ends_with("/**") {
let prefix = &pattern[..pattern.len() - 3];
if let Some(path_str) = path.to_str() {
return path_str.starts_with(prefix)
|| path_str.contains(&format!("{}/", prefix));
}
} else if let Some(file_name) = path.file_name() {
if let Some(file_name_str) = file_name.to_str() {
return glob::Pattern::new(pattern)
.ok()
.map(|p| p.matches(file_name_str))
.unwrap_or(false);
}
}
false
}
fn load_config(config_path: &Option<PathBuf>) -> Result<Config> {
let config_path = if let Some(path) = config_path {
path.clone()
} else {
std::env::current_dir()?
.join("techdebt.yaml")
};
if config_path.exists() {
let content = std::fs::read_to_string(&config_path)?;
let config: Config = serde_yaml::from_str(&content)?;
return Ok(config);
}
Ok(Config::default())
}
pub fn summarize(items: &[TechDebtItem]) -> AnalysisSummary {
let by_priority = ByPriority::from_items(items);
let by_language = ByLanguage::from_items(items);
let complexity_distribution =
crate::models::ComplexityDistribution::from_items(items);
AnalysisSummary {
total_items: items.len(),
by_priority,
by_language,
complexity_distribution,
}
}