backhopper_core/compat/
target_tree.rs1use std::path::{Path, PathBuf};
11
12use crate::compat::target_tree_index::TargetTreeIndex;
13use crate::config::{PathTranslation, PathTranslations, TranslationOrigin};
14use crate::model::verdict::TranslationSource;
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum TargetPathClassification {
18 OnTarget,
20 TranslatesTo {
23 target_path: PathBuf,
24 translation: TranslationSource,
25 },
26 MissingFromTarget,
28 UnsafePath,
31 DeletedByPatch,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct TouchedPathQuery<'a> {
37 pub path: &'a Path,
38 pub is_deletion: bool,
39}
40
41pub fn classify_path(
42 query: TouchedPathQuery<'_>,
43 index: &TargetTreeIndex,
44 translations: &PathTranslations,
45) -> TargetPathClassification {
46 if query.is_deletion {
47 return TargetPathClassification::DeletedByPatch;
48 }
49 let Some(normalised) = normalise(query.path) else {
50 return TargetPathClassification::UnsafePath;
51 };
52 if index.contains_path(&normalised) {
53 return TargetPathClassification::OnTarget;
54 }
55 let path_str = normalised.to_string_lossy();
56 if let Some((rewritten, translation)) = translations.translate(&path_str) {
57 if index.contains_path(&rewritten) {
58 return TargetPathClassification::TranslatesTo {
59 target_path: rewritten,
60 translation: to_verdict_translation_source(translation),
61 };
62 }
63 }
64 TargetPathClassification::MissingFromTarget
65}
66
67fn to_verdict_translation_source(entry: &PathTranslation) -> TranslationSource {
68 match &entry.origin {
69 TranslationOrigin::ConfigStanza => TranslationSource::ConfigStanza {
70 name: entry.name.clone(),
71 },
72 TranslationOrigin::ExternalFile { path } => TranslationSource::ExternalFile {
73 path: path.clone(),
74 name: entry.name.clone(),
75 },
76 }
77}
78
79pub fn normalise(p: &Path) -> Option<PathBuf> {
83 let raw = p.to_string_lossy();
84 let mut s: String = raw.replace('\\', "/");
85 while s.starts_with("./") {
86 s.drain(..2);
87 }
88 if s.split('/').any(|seg| seg == "..") {
89 return None;
90 }
91 Some(PathBuf::from(s))
92}