xbp-analysis 10.57.0

Language-agnostic static analysis (error-handling / Aspirator-style) for XBP
Documentation
//! Inbound ports (application API).

use crate::domain::capabilities::CapabilitySet;
use crate::domain::model::{FileModel, LanguageModel, ParseResult};
use crate::domain::report::AnalysisReport;
use crate::domain::request::{AnalysisConfig, AnalysisRequest};
use crate::domain::rule::{Rule, RuleSelection};
use crate::domain::types::{Finding, LanguageId, SourceFile};
use std::path::Path;

/// Primary application service.
pub trait AnalysisService: Send + Sync {
    fn analyze(&self, request: &AnalysisRequest) -> Result<AnalysisReport, String>;
}

/// Load analysis configuration from project/global sources.
pub trait ConfigPort: Send + Sync {
    fn load(&self, root: &Path) -> Result<AnalysisConfig, String>;
}

/// Select which rules run for a request.
pub trait RuleSelector: Send + Sync {
    fn select(&self, selection: &RuleSelection, caps: &CapabilitySet) -> Vec<Box<dyn Rule>>;
}

/// Filter files / workspace roots.
pub trait FileFilter: Send + Sync {
    fn include(&self, path: &Path, config: &AnalysisConfig) -> bool;
}

/// Apply safe fixes (only when unambiguous).
pub trait FixService: Send + Sync {
    fn apply_safe_fixes(
        &self,
        findings: &[Finding],
        sources: &[(SourceFile, String)],
    ) -> Result<Vec<AppliedFix>, String>;
}

#[derive(Debug, Clone)]
pub struct AppliedFix {
    pub path: std::path::PathBuf,
    pub description: String,
}

/// Query registered language adapters.
pub trait LanguageRegistry: Send + Sync {
    fn supported(&self) -> Vec<LanguageId>;
    fn get(&self, id: &LanguageId) -> Option<&dyn LanguageAdapter>;
}

/// Per-language analysis contract.
pub trait LanguageAdapter: Send + Sync {
    fn id(&self) -> LanguageId;
    fn capabilities(&self) -> CapabilitySet;
    fn detect(&self, path: &Path) -> bool;
    fn parse(&self, source: &SourceFile) -> ParseResult;
    fn build_model(&self, sources: &[SourceFile]) -> Result<LanguageModel, String>;
    fn analyze_language(
        &self,
        model: &LanguageModel,
        rules: &[Box<dyn Rule>],
    ) -> Vec<Finding> {
        let mut out = Vec::new();
        for rule in rules {
            out.extend(rule.evaluate(model));
        }
        out
    }
    /// Optional adapter-local findings (Rust-specific rules).
    fn language_specific_findings(&self, _model: &LanguageModel) -> Vec<Finding> {
        Vec::new()
    }
    /// Map one parsed file into a FileModel (used by build_model).
    fn extract_file_model(&self, source: &SourceFile) -> Result<FileModel, String>;
}