rscheck-cli 0.1.0-alpha.3

CLI frontend for the rscheck policy engine.
Documentation
use crate::analysis::Workspace;
use crate::config::{EngineMode, Policy};
use crate::emit::ReportEmitter;
use crate::report::{AdapterRun, Report};
use crate::rules::{self, RuleBackend, RuleContext};
use crate::semantic::SemanticBackendStatus;

pub struct Runner;

impl Runner {
    pub fn run(ws: &Workspace, policy: &Policy) -> Result<Report, RunError> {
        let semantic_status = SemanticBackendStatus::probe();
        Self::run_with_semantic_status(ws, policy, semantic_status)
    }

    pub fn run_with_semantic_status(
        ws: &Workspace,
        policy: &Policy,
        semantic_status: SemanticBackendStatus,
    ) -> Result<Report, RunError> {
        let mut report = Report {
            rule_catalog: rules::rule_catalog_entries(),
            ..Report::default()
        };

        if policy.engine.semantic == EngineMode::Require && !semantic_status.is_available() {
            return Err(RunError::SemanticBackendRequired(
                semantic_status
                    .reason
                    .clone()
                    .unwrap_or_else(|| "semantic backend unavailable".to_string()),
            ));
        }

        let mut emitter = ReportEmitter::new();
        let ctx = RuleContext { policy };
        for rule in rules::enabled_rules(policy) {
            let info = rule.info();
            if info.backend == RuleBackend::Semantic && !semantic_status.is_available() {
                report.summary.skipped_rules.push(info.id.to_string());
                continue;
            }
            if !report.summary.engine_used.contains(&info.backend) {
                report.summary.engine_used.push(info.backend);
            }
            rule.run(ws, &ctx, &mut emitter);
        }

        report.findings = emitter.findings;
        report.metrics.per_file = emitter.metrics;
        report.summary.semantic_backend_available = semantic_status.is_available();
        report.summary.semantic_backend_reason = semantic_status.reason;
        report.summary.adapter_runs.push(AdapterRun {
            name: "clippy".to_string(),
            enabled: policy.adapters.clippy.enabled,
            toolchain: None,
            status: None,
        });
        Ok(report)
    }
}

#[derive(Debug, thiserror::Error)]
pub enum RunError {
    #[error("{0}")]
    SemanticBackendRequired(String),
}