use std::path::{Path, PathBuf};
use harn_lint::LintSeverity;
use super::outcome::{print_lint_diagnostics, CommandOutcome};
pub(crate) fn collect_prompt_targets(targets: &[&str]) -> Vec<PathBuf> {
let mut files = Vec::new();
for target in targets {
let path = Path::new(target);
if path.is_dir() {
collect_prompt_files(path, &mut files);
} else if is_prompt_file(path) {
files.push(path.to_path_buf());
}
}
files.sort();
files.dedup();
files
}
fn collect_prompt_files(dir: &Path, out: &mut Vec<PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
let mut entries: Vec<_> = entries.filter_map(|e| e.ok()).collect();
entries.sort_by_key(|e| e.path());
for entry in entries {
let path = entry.path();
if path.is_dir() {
collect_prompt_files(&path, out);
} else if is_prompt_file(&path) {
out.push(path);
}
}
}
fn is_prompt_file(path: &Path) -> bool {
let Some(name) = path.file_name().and_then(|n| n.to_str()) else {
return false;
};
name.ends_with(".harn.prompt") || name.ends_with(".prompt")
}
pub(crate) fn lint_prompt_file_inner(
path: &Path,
branch_threshold: Option<usize>,
disabled_rules: &[String],
) -> CommandOutcome {
let path_str = path.to_string_lossy().into_owned();
let source = match std::fs::read_to_string(path) {
Ok(s) => s,
Err(error) => {
eprintln!("error: failed to read {path_str}: {error}");
return CommandOutcome {
has_error: true,
has_warning: false,
};
}
};
let diagnostics = harn_lint::lint_prompt_template(&source, branch_threshold, disabled_rules);
if diagnostics.is_empty() {
println!("{path_str}: no issues found");
return CommandOutcome::default();
}
let has_warning = diagnostics
.iter()
.any(|d| d.severity == LintSeverity::Warning);
let has_error = print_lint_diagnostics(&path_str, &source, &diagnostics);
CommandOutcome {
has_error,
has_warning,
}
}