use std::path::{Path, PathBuf};
use super::format::InputFileType;
use crate::core::data::collect_files;
pub(crate) struct PendingFile {
pub relative_path: String,
pub absolute_path: PathBuf,
}
fn relative_path_string(base: &Path, path: &Path) -> Option<String> {
let relative = path.strip_prefix(base).ok()?;
let components: Vec<&str> = relative
.components()
.filter_map(|c| c.as_os_str().to_str())
.collect();
if components.is_empty() {
return None;
}
Some(components.join("/"))
}
pub(crate) fn gather_pending(
source_dir: &Path,
input_file_type: InputFileType,
) -> Result<Vec<PendingFile>, String> {
let mut pending = Vec::new();
for absolute_path in collect_files(source_dir)? {
let Some(relative_path) = relative_path_string(source_dir, &absolute_path) else {
continue;
};
let actual_extension = absolute_path
.extension()
.and_then(|ext| ext.to_str())
.unwrap_or_default();
if !input_file_type.matches_extension(actual_extension) {
tracing::warn!(
path = %absolute_path.display(),
expected = ?input_file_type.extensions(),
"transform: found an unexpected extension under source/, skipping"
);
continue;
}
pending.push(PendingFile {
relative_path,
absolute_path,
});
}
Ok(pending)
}
#[cfg(test)]
mod tests {
use std::fs;
use super::*;
#[test]
fn gather_pending_filters_by_extension() {
let dir = tempfile::tempdir().unwrap();
let source_dir = dir.path().join("source");
fs::create_dir_all(&source_dir).unwrap();
fs::write(source_dir.join("a.png"), b"a").unwrap();
fs::write(source_dir.join("stray.txt"), b"not a png").unwrap();
let pending = gather_pending(&source_dir, InputFileType::Png).unwrap();
assert_eq!(pending.len(), 1);
assert_eq!(pending[0].relative_path, "a.png");
}
#[test]
fn gather_pending_includes_a_nested_relative_path() {
let dir = tempfile::tempdir().unwrap();
let source_dir = dir.path().join("source");
fs::create_dir_all(source_dir.join("screenshots")).unwrap();
fs::write(source_dir.join("screenshots/img.heic"), b"x").unwrap();
let pending = gather_pending(&source_dir, InputFileType::Heic).unwrap();
assert_eq!(pending.len(), 1);
assert_eq!(pending[0].relative_path, "screenshots/img.heic");
}
#[test]
fn gather_pending_accepts_every_spelling_of_one_input_kind() {
let dir = tempfile::tempdir().unwrap();
let source_dir = dir.path().join("source");
fs::create_dir_all(&source_dir).unwrap();
fs::write(source_dir.join("a.jpg"), b"a").unwrap();
fs::write(source_dir.join("b.jpeg"), b"b").unwrap();
fs::write(source_dir.join("c.JPG"), b"c").unwrap();
fs::write(source_dir.join("stray.png"), b"a different format").unwrap();
let pending = gather_pending(&source_dir, InputFileType::Jpeg).unwrap();
let mut found: Vec<String> = pending.into_iter().map(|file| file.relative_path).collect();
found.sort();
assert_eq!(found, vec!["a.jpg", "b.jpeg", "c.JPG"]);
}
#[test]
fn gather_pending_filters_by_extension_for_a_video_input_kind() {
let dir = tempfile::tempdir().unwrap();
let source_dir = dir.path().join("source");
fs::create_dir_all(&source_dir).unwrap();
fs::write(source_dir.join("a.mp4"), b"a").unwrap();
fs::write(source_dir.join("stray.mov"), b"not an mp4").unwrap();
let pending = gather_pending(&source_dir, InputFileType::Mp4).unwrap();
assert_eq!(pending.len(), 1);
assert_eq!(pending[0].relative_path, "a.mp4");
}
#[test]
fn gather_pending_includes_every_matching_file_regardless_of_prior_state() {
let dir = tempfile::tempdir().unwrap();
let source_dir = dir.path().join("source");
fs::create_dir_all(&source_dir).unwrap();
fs::write(source_dir.join("a.png"), b"a").unwrap();
fs::write(source_dir.join("b.png"), b"b").unwrap();
let pending = gather_pending(&source_dir, InputFileType::Png).unwrap();
assert_eq!(pending.len(), 2);
}
}