use std::collections::HashMap;
use crate::entry::Entry;
use super::Filter;
pub(crate) type SubtreeMatch = HashMap<*const Entry, bool>;
pub(crate) fn precompute_subtree_match(entry: &Entry, filter: &Filter) -> SubtreeMatch {
let mut map: SubtreeMatch = HashMap::new();
walk_match(entry, filter, &mut map);
map
}
fn walk_match(entry: &Entry, filter: &Filter, map: &mut SubtreeMatch) -> bool {
let mut any = filter.matches(entry);
if let Some(children) = entry.children() {
for child in children {
let child_has = walk_match(child, filter, map);
any = any || child_has;
}
}
map.insert(entry as *const Entry, any);
any
}
pub(crate) fn subtree_visible(entry: &Entry, map: &SubtreeMatch) -> bool {
map.get(&(entry as *const Entry)).copied().unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::classify::Category;
use crate::filter::FilterInputs;
fn dir(name: &str, cat: Category, children: Vec<Entry>) -> Entry {
Entry::dir(name.to_string(), cat, None, children)
}
fn file(name: &str, size: u64, cat: Category, days_ago: Option<u64>) -> Entry {
Entry::file(name.to_string(), size, cat, days_ago)
}
#[test]
fn precompute_subtree_match_keeps_ancestors_of_matches() {
let tree = dir(
"root",
Category::Other,
vec![
dir(
"src",
Category::Other,
vec![file("main.rs", 10, Category::Other, None)],
),
dir(
"target",
Category::Build,
vec![dir(
"debug",
Category::Build,
vec![file("app", 100, Category::Build, None)],
)],
),
file("notes.txt", 5, Category::Other, None),
],
);
let f = Filter::from_inputs(FilterInputs {
categories: vec![],
type_: None,
min_size: None,
names: vec!["*.rs".into()],
changed_within: None,
changed_before: None,
})
.unwrap();
let map = precompute_subtree_match(&tree, &f);
let root_ptr = &tree as *const Entry;
assert!(*map.get(&root_ptr).unwrap());
let src = tree
.children()
.unwrap()
.iter()
.find(|c| c.name == "src")
.unwrap();
assert!(*map.get(&(src as *const Entry)).unwrap());
let main_rs = &src.children().unwrap()[0];
assert!(*map.get(&(main_rs as *const Entry)).unwrap());
let target = tree
.children()
.unwrap()
.iter()
.find(|c| c.name == "target")
.unwrap();
assert!(!*map.get(&(target as *const Entry)).unwrap());
let notes = tree
.children()
.unwrap()
.iter()
.find(|c| c.name == "notes.txt")
.unwrap();
assert!(!*map.get(&(notes as *const Entry)).unwrap());
}
}