use crate::NormalizedKey;
use ahash::AHashMap;
use std::{collections::BTreeMap, path::PathBuf};
pub const VFS_LOCK_SCHEMA_VERSION: u32 = 1;
mod candidate;
mod drift;
mod impact;
mod layer;
mod lock;
mod provenance;
mod provider_io;
mod semantic_conflicts;
mod simulate;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", serde(rename_all = "snake_case"))]
#[non_exhaustive]
pub enum SourceKind {
LooseDir,
Archive,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct SourceMeta {
pub path: PathBuf,
pub kind: SourceKind,
}
#[derive(Debug, Clone)]
pub struct LayerIndex {
pub sources: Vec<SourceMeta>,
path_to_sources: AHashMap<NormalizedKey, Vec<usize>>,
provider_paths: AHashMap<(usize, NormalizedKey), Vec<PathBuf>>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct LayerProvider {
pub source_index: usize,
pub provider_index: usize,
pub source: SourceMeta,
pub key: std::path::PathBuf,
pub original_path: std::path::PathBuf,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct SourceContribution {
pub source_index: usize,
pub source: SourceMeta,
pub winning_files: usize,
pub overriding_files: usize,
pub overridden_files: usize,
pub unique_files: usize,
pub duplicate_files: usize,
pub loose_files: usize,
pub archive_files: usize,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct SourceContributionReport {
pub sources: Vec<SourceContribution>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct ProviderRecord {
pub source: SourceMeta,
pub resolved_path: String,
pub hash_blake3: Option<String>,
pub size: Option<u64>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct ProvenanceChain {
pub key: PathBuf,
pub providers: Vec<ProviderRecord>,
pub winner: SourceMeta,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct VfsLock {
pub schema_version: u32,
pub entries: Vec<VfsLockEntry>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct VfsLockEntry {
pub key: PathBuf,
pub winner_source: PathBuf,
pub winner_kind: SourceKind,
pub winner_hash_blake3: Option<String>,
pub winner_size: Option<u64>,
pub provider_count: usize,
}
#[derive(Debug, Clone)]
pub enum ReorderOp {
Swap(PathBuf, PathBuf),
MoveBefore {
source: PathBuf,
before: PathBuf,
},
MoveAfter {
source: PathBuf,
after: PathBuf,
},
FullOrder(Vec<PathBuf>),
}
#[derive(Debug, Clone)]
pub struct SimOpts {
pub sample_limit: usize,
pub impact_buckets: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", serde(rename_all = "snake_case"))]
pub enum HeuristicCondition {
WinnerChanged,
WinnerChangedAndSemanticBehaviorChanging,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ImpactHeuristic {
pub name: String,
pub path_glob: String,
pub weight: f32,
pub condition: HeuristicCondition,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ImpactProfile {
pub heuristics: Vec<ImpactHeuristic>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct RiskyChange {
pub key: PathBuf,
pub score: f32,
pub reasons: Vec<String>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct BucketImpact {
pub bucket: String,
pub score: f32,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ImpactReport {
pub overall_score: f32,
pub risk_level: RiskLevel,
pub by_bucket: Vec<BucketImpact>,
pub top_risky_changes: Vec<RiskyChange>,
}
impl Default for SimOpts {
fn default() -> Self {
Self {
sample_limit: 100,
impact_buckets: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct BucketDelta {
pub bucket: String,
pub changed_winners: usize,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct SourceDelta {
pub source: PathBuf,
pub wins_before: usize,
pub wins_after: usize,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct SimulationDelta {
pub changed_winners: usize,
pub unchanged_winners: usize,
pub by_source_gain_loss: Vec<SourceDelta>,
pub by_bucket: Vec<BucketDelta>,
pub changed_keys_sample: Vec<PathBuf>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "serialize", serde(rename_all = "snake_case"))]
pub enum DriftKind {
Added,
Removed,
WinnerSourceChanged,
WinnerHashChanged,
ProviderCountChanged,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct DriftEntry {
pub key: PathBuf,
pub kind: DriftKind,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct DriftReport {
pub entries: Vec<DriftEntry>,
pub counts: BTreeMap<DriftKind, usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", serde(rename_all = "snake_case"))]
pub enum RiskLevel {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Copy)]
pub struct CandidatePlanOpts {
pub include_semantic: bool,
}
impl Default for CandidatePlanOpts {
fn default() -> Self {
Self {
include_semantic: true,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct CandidateConflict {
pub key: PathBuf,
pub current_winner_source: PathBuf,
pub candidate_file: PathBuf,
pub semantic_differs: Option<bool>,
pub risk: Option<RiskLevel>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct CandidatePlanSummary {
pub additions: usize,
pub conflicts: usize,
pub displaced_winners: usize,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct CandidatePlan {
pub additions: Vec<PathBuf>,
pub conflicts: Vec<CandidateConflict>,
pub displaced_winners: Vec<PathBuf>,
pub summary: CandidatePlanSummary,
}
#[cfg(test)]
#[path = "analysis/tests.rs"]
mod tests;