use std::{fs::File, path::Path};
fn copy_directory(source: &Path, target: &Path) -> std::io::Result<()> {
let mut stack = Vec::new();
stack.push((source.to_path_buf(), target.to_path_buf()));
while let Some((src, dst)) = stack.pop() {
if src.is_dir() {
std::fs::create_dir_all(&dst)?;
for entry in std::fs::read_dir(&src)? {
let entry = entry?;
let entry_src = entry.path();
let entry_dst = dst.join(entry_src.file_name().unwrap());
if entry_src.is_dir() {
stack.push((entry_src, entry_dst));
} else if entry_src.is_file() {
std::fs::copy(entry_src, entry_dst)?;
}
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use assert_cmd::Command;
use predicates::prelude::*;
use std::path::PathBuf;
use tempdir::TempDir;
#[test]
fn test_cmd_help() {
let mut cmd = Command::cargo_bin("clineup").unwrap();
let assert = cmd.arg("--help").assert();
assert.success().stdout(predicates::str::contains("USAGE"));
}
#[test]
fn test_cmd_missing_folder_or_filename() {
let mut cmd = Command::cargo_bin("clineup").unwrap();
let assert = cmd
.arg("--source=whatever")
.arg("--destination=whatever")
.assert();
assert.failure().stdout(predicates::str::contains(
"You should provide at least one of the folder or filename format.",
));
}
#[test]
fn test_cmd_missing_folder_or_filename_but_verbose() {
let mut cmd = Command::cargo_bin("clineup").unwrap();
let assert = cmd
.arg("--source=whatever")
.arg("--destination=whatever")
.arg("-vv")
.assert();
assert
.failure()
.stderr(predicates::str::contains("DEBUG"))
.stderr(predicates::str::contains("Get config"));
}
#[test]
fn test_cmd_copy_strategy() {
let mut cmd = Command::cargo_bin("clineup").unwrap();
let data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/");
let input_tmp_dir = TempDir::new("input_copy_strategy").unwrap();
let output_tmp_dir = TempDir::new("output_copy_strategy").unwrap();
copy_directory(&data_path, &input_tmp_dir.path()).unwrap();
let assert = cmd
.arg(format!(
"--source={}",
format!("{}", input_tmp_dir.path().to_string_lossy())
))
.arg(format!(
"--destination={}",
output_tmp_dir.path().to_string_lossy()
))
.arg("--folder-format={%year}")
.arg("--strategy=copy")
.arg("-vv")
.assert();
assert.success();
let expected_2023 = Path::new(output_tmp_dir.path())
.join("2023")
.join("Paris-20230304.jpg");
assert!(expected_2023.exists());
let expected_unknown_year = Path::new(output_tmp_dir.path())
.join("Unknown Year")
.join("Paris.png");
assert!(expected_unknown_year.exists());
let still_exist = Path::new(input_tmp_dir.path()).join("Paris-20230304.jpg");
assert!(still_exist.exists());
let expected_number_of_files = 3;
let entries = std::fs::read_dir(Path::new(output_tmp_dir.path()).join("2023"))
.unwrap()
.count();
assert!(entries == expected_number_of_files)
}
#[test]
fn test_cmd_move_strategy() {
let mut cmd = Command::cargo_bin("clineup").unwrap();
let data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/");
let input_tmp_dir = TempDir::new("input_move_strategy").unwrap();
let output_tmp_dir = TempDir::new("output_move_strategy").unwrap();
copy_directory(&data_path, &input_tmp_dir.path()).unwrap();
let assert = cmd
.arg(format!(
"--source={}",
format!("{}", input_tmp_dir.path().to_string_lossy())
))
.arg(format!(
"--destination={}",
output_tmp_dir.path().to_string_lossy()
))
.arg("--folder-format={%year}")
.arg("--strategy=move")
.arg("-vv")
.assert();
assert.success();
let expected_2023 = Path::new(output_tmp_dir.path())
.join("2023")
.join("Paris-20230304.jpg");
assert!(expected_2023.exists());
let expected_unknown_year = Path::new(output_tmp_dir.path())
.join("Unknown Year")
.join("Paris.png");
assert!(expected_unknown_year.exists());
let should_not_exist = Path::new(input_tmp_dir.path()).join("Paris-20230304.jpg");
assert!(!should_not_exist.exists());
let expected_number_of_files = 3;
let entries = std::fs::read_dir(Path::new(output_tmp_dir.path()).join("2023"))
.unwrap()
.count();
assert!(entries == expected_number_of_files)
}
#[test]
fn test_cmd_symlink_strategy() {
let mut cmd = Command::cargo_bin("clineup").unwrap();
let data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/");
let input_tmp_dir = TempDir::new("input_symlink_strategy").unwrap();
let output_tmp_dir = TempDir::new("output_symlink_strategy").unwrap();
copy_directory(&data_path, &input_tmp_dir.path()).unwrap();
let assert = cmd
.arg(format!(
"--source={}",
format!("{}", input_tmp_dir.path().to_string_lossy())
))
.arg(format!(
"--destination={}",
output_tmp_dir.path().to_string_lossy()
))
.arg("--folder-format={%year}")
.arg("--strategy=move")
.arg("-vv")
.assert();
assert.success();
let expected = Path::new(output_tmp_dir.path())
.join("2023")
.join("Paris-20230304.jpg");
assert!(expected.exists());
let expected_number_of_files = 3;
let entries = std::fs::read_dir(Path::new(output_tmp_dir.path()).join("2023"))
.unwrap()
.count();
assert!(entries == expected_number_of_files)
}
#[test]
fn test_cmd_copy_strategy_recursive() {
let mut cmd = Command::cargo_bin("clineup").unwrap();
let data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/");
let input_tmp_dir = TempDir::new("input_copy_strategy").unwrap();
let output_tmp_dir = TempDir::new("output_copy_strategy").unwrap();
copy_directory(&data_path, &input_tmp_dir.path()).unwrap();
let assert = cmd
.arg(format!(
"--source={}",
format!("{}", input_tmp_dir.path().to_string_lossy())
))
.arg(format!(
"--destination={}",
output_tmp_dir.path().to_string_lossy()
))
.arg("--folder-format={%year}")
.arg("--strategy=copy")
.arg("--recursive")
.arg("-vv")
.assert();
assert.success();
for folder in 2013..2023 {
let path = Path::new(output_tmp_dir.path()).join(format!("{}", folder));
assert!(path.exists())
}
}
#[test]
fn test_cmd_copy_strategy_recursive_exclude_regex() {
let mut cmd = Command::cargo_bin("clineup").unwrap();
let data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/");
let input_tmp_dir = TempDir::new("input_copy_strategy").unwrap();
let output_tmp_dir = TempDir::new("output_copy_strategy").unwrap();
copy_directory(&data_path, &input_tmp_dir.path()).unwrap();
let assert = cmd
.arg(format!(
"--source={}",
format!("{}", input_tmp_dir.path().to_string_lossy())
))
.arg(format!(
"--destination={}",
output_tmp_dir.path().to_string_lossy()
))
.arg("--folder-format={%year}")
.arg("--strategy=copy")
.arg("--recursive")
.arg("--exclude-regex=.*Paris-20130101.jpg.*")
.arg("-vv")
.assert();
assert.success();
let should_not_exist = Path::new(output_tmp_dir.path()).join("2013");
assert!(!should_not_exist.exists());
for folder in 2014..2023 {
let path = Path::new(output_tmp_dir.path()).join(format!("{}", folder));
assert!(path.exists())
}
}
#[test]
fn test_cmd_copy_strategy_recursive_include_regex() {
let mut cmd = Command::cargo_bin("clineup").unwrap();
let data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/");
let input_tmp_dir = TempDir::new("input_copy_strategy").unwrap();
let output_tmp_dir = TempDir::new("output_copy_strategy").unwrap();
copy_directory(&data_path, &input_tmp_dir.path()).unwrap();
let assert = cmd
.arg(format!(
"--source={}",
format!("{}", input_tmp_dir.path().to_string_lossy())
))
.arg(format!(
"--destination={}",
output_tmp_dir.path().to_string_lossy()
))
.arg("--folder-format={%year}")
.arg("--strategy=copy")
.arg("--recursive")
.arg("--include-regex=.*Paris-20130101.jpg.*")
.arg("-vv")
.assert();
assert.success();
let should_exist = Path::new(output_tmp_dir.path()).join("2013");
assert!(should_exist.exists());
for folder in 2014..2023 {
let path = Path::new(output_tmp_dir.path()).join(format!("{}", folder));
assert!(!path.exists())
}
}
}