#[cfg(test)]
use std::collections::BTreeSet;
#[cfg(test)]
#[must_use]
pub(crate) fn directory_prefix(rel_dir: &str) -> String {
if rel_dir.is_empty() {
String::new()
} else {
format!("{rel_dir}/")
}
}
#[cfg(test)]
#[must_use]
pub(crate) fn unique_indexed_subdirectory(
tracked: &BTreeSet<String>,
gitlinks: &BTreeSet<String>,
rel_dir: &str,
) -> Option<String> {
let prefix = directory_prefix(rel_dir);
let mut component: Option<String> = None;
let mut direct_file = false;
for path in tracked.range(prefix.clone()..) {
if !path.starts_with(&prefix) {
break;
}
let rest = path.strip_prefix(&prefix).unwrap_or("");
if rest.is_empty() {
continue;
}
match rest.split_once('/') {
Some((first, _)) => match &component {
None => component = Some(first.to_owned()),
Some(c) if c == first => {}
_ => return None,
},
None => {
direct_file = true;
}
}
}
for g in gitlinks {
if g.as_str() == rel_dir {
continue;
}
if !g.starts_with(&prefix) {
continue;
}
let rest = g.strip_prefix(&prefix).unwrap_or("");
if rest.is_empty() {
continue;
}
let first = rest.split('/').next().unwrap_or("");
if first.is_empty() {
continue;
}
match &component {
None => component = Some(first.to_owned()),
Some(c) if c == first => {}
_ => return None,
}
}
if direct_file {
None
} else {
component
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unique_subdir_for_linear_chain() {
let tracked: BTreeSet<String> = ["a/b/c/file.txt".to_owned()].into_iter().collect();
let gitlinks = BTreeSet::new();
assert_eq!(
unique_indexed_subdirectory(&tracked, &gitlinks, ""),
Some("a".to_owned())
);
assert_eq!(
unique_indexed_subdirectory(&tracked, &gitlinks, "a"),
Some("b".to_owned())
);
assert_eq!(
unique_indexed_subdirectory(&tracked, &gitlinks, "a/b"),
Some("c".to_owned())
);
assert_eq!(
unique_indexed_subdirectory(&tracked, &gitlinks, "a/b/c"),
None
);
}
#[test]
fn unique_subdir_none_when_sibling_files() {
let tracked: BTreeSet<String> = ["a/x.txt".to_owned(), "a/y.txt".to_owned()]
.into_iter()
.collect();
let gitlinks = BTreeSet::new();
assert_eq!(unique_indexed_subdirectory(&tracked, &gitlinks, "a"), None);
}
}