strixonomy_diagnostics/
engine.rs1use crate::config::DiagnosticConfig;
2use crate::input::DiagnosticInput;
3use crate::rules::{
4 broken_imports, duplicate_labels, missing_labels, orphan_classes, parse_errors,
5 undefined_prefixes,
6};
7use std::cell::RefCell;
8use std::collections::HashMap;
9use std::path::PathBuf;
10use strixonomy_core::{
11 read_to_string_capped, Diagnostic, DiagnosticCode, DiagnosticSeverity, SourceLocation,
12 MAX_FILE_BYTES,
13};
14
15pub fn collect_diagnostics(input: &DiagnosticInput<'_>) -> Vec<Diagnostic> {
17 collect_diagnostics_with_config(input, &HashMap::new(), None)
18}
19
20pub fn collect_diagnostics_with_config(
22 input: &DiagnosticInput<'_>,
23 source_overrides: &HashMap<PathBuf, String>,
24 config: Option<&DiagnosticConfig>,
25) -> Vec<Diagnostic> {
26 let mut diagnostics = collect_diagnostics_with_sources(input, source_overrides);
27 if let Some(cfg) = config {
28 diagnostics.retain(|d| cfg.is_rule_enabled(d.code));
29 for d in &mut diagnostics {
30 if let Some(sev) = cfg.severity_override(d.code) {
31 d.severity = sev;
32 }
33 }
34 }
35 diagnostics
36}
37
38pub fn collect_diagnostics_with_sources(
40 input: &DiagnosticInput<'_>,
41 source_overrides: &HashMap<PathBuf, String>,
42) -> Vec<Diagnostic> {
43 let io_failures: RefCell<Vec<Diagnostic>> = RefCell::new(Vec::new());
44
45 let source = |path: &std::path::Path| -> String {
46 if let Some(text) = source_overrides.get(path) {
47 return text.clone();
48 }
49 if let Ok(canonical) = path.canonicalize() {
50 if let Some(text) = source_overrides.get(&canonical) {
51 return text.clone();
52 }
53 }
54 for (override_path, text) in source_overrides {
55 if strixonomy_core::paths_refer_to_same(override_path, path) {
56 return text.clone();
57 }
58 }
59 match read_to_string_capped(path, MAX_FILE_BYTES) {
60 Ok(text) => text,
61 Err(err) => {
62 io_failures.borrow_mut().push(Diagnostic {
63 code: DiagnosticCode::IoReadError,
64 severity: DiagnosticSeverity::Warning,
65 message: format!("could not read file for lint analysis: {err}"),
66 file: path.to_path_buf(),
67 range: SourceLocation::default(),
68 entity_iri: None,
69 quick_fix: None,
70 plugin_id: None,
71 plugin_code: None,
72 });
73 String::new()
74 }
75 }
76 };
77
78 let mut diagnostics = Vec::new();
79 diagnostics.extend(parse_errors(input, &source));
80 diagnostics.extend(broken_imports(input, &source));
81 diagnostics.extend(undefined_prefixes(input, &source));
82 diagnostics.extend(duplicate_labels(input, &source));
83 diagnostics.extend(missing_labels(input, &source));
84 diagnostics.extend(orphan_classes(input, &source));
85 diagnostics.extend(io_failures.into_inner());
86 diagnostics
87}