///|
pub async fn find_home_target_file(
home_dir : String,
file_name : String,
) -> String {
let target_file = @path.Path::join(home_dir, file_name).to_string()
guard @fs.exists(target_file) else {
fail("Could not find home-level file: \{file_name}")
}
target_file
}
///|
pub async fn find_parent_target_file(
file_name : String,
top_dir : String,
start_dir : String,
) -> String {
let boundary_dir = normalize_path(top_dir)
let current_dir = normalize_path(start_dir)
find_parent_target_file_from(file_name, boundary_dir, current_dir)
}
///|
async fn find_parent_target_file_from(
file_name : String,
boundary_dir : String,
current_dir : String,
) -> String {
let target_file = @path.Path::join(current_dir, file_name).to_string()
if @fs.exists(target_file) {
return target_file
}
guard current_dir != boundary_dir else {
fail("Could not find ancestor file: \{file_name}")
}
let parent_dir = @path.Path::dirname(current_dir).to_string()
guard parent_dir != current_dir else {
fail("Reached filesystem root before top directory: \{boundary_dir}")
}
find_parent_target_file_from(file_name, boundary_dir, parent_dir)
}
///|
pub async fn collect_recursive_target_files(
start_dir : String,
file_name : String,
) -> ReadOnlyArray[String] {
ReadOnlyArray::from_array(
collect_recursive_target_files_from(start_dir, file_name, []),
)
}
///|
async fn collect_recursive_target_files_from(
dir : String,
file_name : String,
inherited_ignore_rules : Array[IgnoreRuleSet],
) -> Array[String] {
let target_file = @path.Path::join(dir, file_name).to_string()
let (local_ignore_rules, has_target_file, entries) = @async.with_task_group(group => {
let ignore_task = group.spawn(() => read_ignore_rules(dir))
let target_task = group.spawn(() => @fs.exists(target_file))
let entries_task = group.spawn(() => {
@fs.readdir(dir, include_hidden=true, include_special=false, sort=true)
})
(ignore_task.wait(), target_task.wait(), entries_task.wait())
})
let ignore_rules = match local_ignore_rules {
Some(rules) =>
inherited_ignore_rules.iter().concat([rules].iter()).collect()
None => inherited_ignore_rules
}
let local_files = if has_target_file { [target_file] } else { [] }
let descendant_files = collect_descendant_target_files(
entries, dir, file_name, ignore_rules,
)
local_files.iter().concat(descendant_files.iter()).collect()
}
///|
async fn collect_descendant_target_files(
entries : Array[String],
dir : String,
file_name : String,
ignore_rules : Array[IgnoreRuleSet],
) -> Array[String] {
@async.all(
entries.map(entry => {
() => {
let entry_path = @path.Path::join(dir, entry).to_string()
if @fs.kind(entry_path) == @fs.FileKind::Directory &&
!is_ignored_directory(entry_path, ignore_rules) {
collect_recursive_target_files_from(
entry_path, file_name, ignore_rules,
)
} else {
[]
}
}
}),
)
.iter()
.flat_map(files => files.iter())
.collect()
}