Skip to main content

rscheck_cli/
runner.rs

1use crate::analysis::Workspace;
2use crate::config::{EngineMode, Policy};
3use crate::emit::ReportEmitter;
4use crate::report::{AdapterRun, Report};
5use crate::rules::{self, RuleBackend, RuleContext};
6use crate::semantic::SemanticBackendStatus;
7
8pub struct Runner;
9
10impl Runner {
11    pub fn run(ws: &Workspace, policy: &Policy) -> Result<Report, RunError> {
12        let semantic_status = SemanticBackendStatus::probe();
13        Self::run_with_semantic_status(ws, policy, semantic_status)
14    }
15
16    pub fn run_with_semantic_status(
17        ws: &Workspace,
18        policy: &Policy,
19        semantic_status: SemanticBackendStatus,
20    ) -> Result<Report, RunError> {
21        let mut report = Report {
22            rule_catalog: rules::rule_catalog_entries(),
23            ..Report::default()
24        };
25
26        if policy.engine.semantic == EngineMode::Require && !semantic_status.is_available() {
27            return Err(RunError::SemanticBackendRequired(
28                semantic_status
29                    .reason
30                    .clone()
31                    .unwrap_or_else(|| "semantic backend unavailable".to_string()),
32            ));
33        }
34
35        let mut emitter = ReportEmitter::new();
36        let ctx = RuleContext { policy };
37        for rule in rules::enabled_rules(policy) {
38            let info = rule.info();
39            if info.backend == RuleBackend::Semantic && !semantic_status.is_available() {
40                report.summary.skipped_rules.push(info.id.to_string());
41                continue;
42            }
43            if !report.summary.engine_used.contains(&info.backend) {
44                report.summary.engine_used.push(info.backend);
45            }
46            rule.run(ws, &ctx, &mut emitter);
47        }
48
49        report.findings = emitter.findings;
50        report.metrics.per_file = emitter.metrics;
51        report.summary.semantic_backend_available = semantic_status.is_available();
52        report.summary.semantic_backend_reason = semantic_status.reason;
53        report.summary.adapter_runs.push(AdapterRun {
54            name: "clippy".to_string(),
55            enabled: policy.adapters.clippy.enabled,
56            toolchain: None,
57            status: None,
58        });
59        Ok(report)
60    }
61}
62
63#[derive(Debug, thiserror::Error)]
64pub enum RunError {
65    #[error("{0}")]
66    SemanticBackendRequired(String),
67}