use std::path::{Path, PathBuf};
use crate::compat::target_tree_index::TargetTreeIndex;
use crate::config::{PathTranslation, PathTranslations, TranslationOrigin};
use crate::model::verdict::TranslationSource;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TargetPathClassification {
OnTarget,
TranslatesTo {
target_path: PathBuf,
translation: TranslationSource,
},
MissingFromTarget,
UnsafePath,
DeletedByPatch,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TouchedPathQuery<'a> {
pub path: &'a Path,
pub is_deletion: bool,
}
pub fn classify_path(
query: TouchedPathQuery<'_>,
index: &TargetTreeIndex,
translations: &PathTranslations,
) -> TargetPathClassification {
if query.is_deletion {
return TargetPathClassification::DeletedByPatch;
}
let Some(normalised) = normalise(query.path) else {
return TargetPathClassification::UnsafePath;
};
if index.contains_path(&normalised) {
return TargetPathClassification::OnTarget;
}
let path_str = normalised.to_string_lossy();
if let Some((rewritten, translation)) = translations.translate(&path_str) {
if index.contains_path(&rewritten) {
return TargetPathClassification::TranslatesTo {
target_path: rewritten,
translation: to_verdict_translation_source(translation),
};
}
}
TargetPathClassification::MissingFromTarget
}
fn to_verdict_translation_source(entry: &PathTranslation) -> TranslationSource {
match &entry.origin {
TranslationOrigin::ConfigStanza => TranslationSource::ConfigStanza {
name: entry.name.clone(),
},
TranslationOrigin::ExternalFile { path } => TranslationSource::ExternalFile {
path: path.clone(),
name: entry.name.clone(),
},
}
}
pub fn normalise(p: &Path) -> Option<PathBuf> {
let raw = p.to_string_lossy();
let mut s: String = raw.replace('\\', "/");
while s.starts_with("./") {
s.drain(..2);
}
if s.split('/').any(|seg| seg == "..") {
return None;
}
Some(PathBuf::from(s))
}