use std::path::{Path, PathBuf};
#[derive(Debug, Default)]
pub struct WasmDirScan {
pub sorted_paths: Vec<PathBuf>,
pub entry_failures: Vec<(PathBuf, std::io::Error)>,
}
pub fn collect_wasm_paths(dir: &Path) -> std::io::Result<WasmDirScan> {
let entries = std::fs::read_dir(dir)?;
let mut scan = WasmDirScan::default();
for entry in entries {
match entry {
Ok(e) => {
let path = e.path();
if path.is_file()
&& path
.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("wasm"))
{
scan.sorted_paths.push(path);
}
}
Err(source) => {
scan.entry_failures.push((dir.to_path_buf(), source));
}
}
}
scan.sorted_paths.sort();
Ok(scan)
}
#[cfg(test)]
mod tests {
use super::*;
fn touch(path: &Path) {
std::fs::write(path, b"placeholder").expect("write fixture");
}
#[test]
fn collects_only_top_level_wasm_files_in_sorted_order() {
let dir = tempfile::tempdir().expect("tempdir");
let p = dir.path();
touch(&p.join("b_second.wasm"));
touch(&p.join("a_first.wasm"));
touch(&p.join("README.md")); touch(&p.join(".gitignore")); std::fs::create_dir(p.join("sub")).expect("subdir");
touch(&p.join("sub").join("recursed.wasm"));
let scan = collect_wasm_paths(p).expect("scan succeeds");
let names: Vec<_> = scan
.sorted_paths
.iter()
.map(|p| p.file_name().unwrap().to_string_lossy().into_owned())
.collect();
assert_eq!(names, vec!["a_first.wasm", "b_second.wasm"]);
assert!(scan.entry_failures.is_empty());
}
#[test]
fn extension_match_is_case_insensitive() {
let dir = tempfile::tempdir().expect("tempdir");
touch(&dir.path().join("upper.WASM"));
touch(&dir.path().join("mixed.Wasm"));
let scan = collect_wasm_paths(dir.path()).expect("scan succeeds");
assert_eq!(scan.sorted_paths.len(), 2);
}
#[test]
fn missing_dir_propagates_read_dir_error() {
let dir = tempfile::tempdir().expect("tempdir");
let nonexistent = dir.path().join("does-not-exist");
let err = collect_wasm_paths(&nonexistent)
.expect_err("missing dir should error at the read_dir step, not in entry_failures");
assert_eq!(err.kind(), std::io::ErrorKind::NotFound);
}
}