pub struct Strategy {} use std::fs::{copy, create_dir_all}; use std::io::{Error, ErrorKind, Result}; use std::path::Path; use crate::file_handler::{File, FileType, Strategy as FileHandlerStrategy}; impl Strategy { fn handle(source: &Path, destination: &Path, file: &File) -> Result<()> { let relative_path = file.path.strip_prefix(source).map_err(|_| { Error::new( ErrorKind::InvalidData, "Path was not part of source directory.", ) })?; let complete_destination = destination.join(relative_path); match complete_destination.parent() { Some(destination_parent) => { create_dir_all(destination_parent)?; copy(&file.path, &complete_destination)?; Ok(()) } None => Err(Error::new( ErrorKind::InvalidData, "Destination parent was not readable.", )), } } } impl FileHandlerStrategy for Strategy { fn is(&self, path: &Path) -> bool { !path.is_dir() } fn identify(&self) -> FileType { FileType::File } fn can_handle(&self, file_type: &FileType) -> bool { matches!(file_type, FileType::File) } fn handle_html(&self, source: &Path, destination: &Path, file: &File, _l: &str) -> Result<()> { Strategy::handle(source, destination, file) } fn handle_gemini(&self, source: &Path, destination: &Path, file: &File) -> Result<()> { Strategy::handle(source, destination, file) } } #[cfg(test)] mod tests { use std::fs; use super::*; use test_utilities::*; #[test] fn identifies_regular_files() { let test_dir = setup_test_dir(); create_test_file(&test_dir.join("image.png"), ""); create_test_file(&test_dir.join("style.css"), ""); let strategy = Strategy {}; assert!(strategy.is(&test_dir.join("image.png"))); assert!(strategy.is(&test_dir.join("style.css"))); } #[test] fn rejects_directories() { let test_dir = setup_test_dir(); let strategy = Strategy {}; assert!(!strategy.is(&test_dir)); } #[test] fn identifies_file_type() { let strategy = Strategy {}; assert!(matches!(strategy.identify(), FileType::File)); } #[test] fn handles_file_type() { let strategy = Strategy {}; assert!(strategy.can_handle(&FileType::File)); } #[test] fn rejects_non_file_types() { let strategy = Strategy {}; assert!(!strategy.can_handle(&FileType::Layout)); assert!(!strategy.can_handle(&FileType::Gemini)); assert!(!strategy.can_handle(&FileType::Unknown)); } #[test] fn copies_single_file() { let test_dir = setup_test_dir(); let source_dir = test_dir.join("source"); let output_dir = test_dir.join("output"); create_dir_all(&source_dir).expect("Could not create source test directory"); create_dir_all(&output_dir).expect("Could not create output test directory"); create_test_file(&source_dir.join("image.png"), "A fish playing the banjo"); let file = File { path: source_dir.join("image.png"), file_type: FileType::File, }; assert!(Strategy::handle(&source_dir, &output_dir, &file).is_ok()); let copied_path = &output_dir.join("image.png"); assert!(copied_path.exists()); // Verify file contents are identical let original = fs::read(&file.path).unwrap(); let copied = fs::read(copied_path).unwrap(); assert_eq!(original, copied); } #[test] fn copies_nested_file() { let test_dir = setup_test_dir(); let source_dir = test_dir.join("source"); let output_dir = test_dir.join("output"); create_dir_all(&source_dir).expect("Could not create source test directory"); create_dir_all(&output_dir).expect("Could not create output test directory"); create_dir_all(source_dir.join("nested")).expect("Could not create source test directory"); create_test_file( &source_dir.join("nested/style.css"), "* { margin: 0; padding: 0 }", ); let file = File { path: source_dir.join("nested/style.css"), file_type: FileType::File, }; assert!(Strategy::handle(&source_dir, &output_dir, &file).is_ok()); let copied_path = output_dir.join("nested/style.css"); assert!(copied_path.exists()); // Verify file contents are identical let original = fs::read(&file.path).unwrap(); let copied = fs::read(&copied_path).unwrap(); assert_eq!(original, copied); } #[test] fn handle_html_copies_file() { let test_dir = setup_test_dir(); let source_dir = test_dir.join("source"); let output_dir = test_dir.join("output"); create_dir_all(&source_dir).expect("Could not create source test directory"); create_dir_all(&output_dir).expect("Could not create output test directory"); create_test_file(&source_dir.join("image.png"), "A fish playing the banjo"); let strategy = Strategy {}; let file = File { path: source_dir.join("image.png"), file_type: FileType::File, }; assert!( strategy .handle_html(&source_dir, &output_dir, &file, "unused layout") .is_ok() ); let copied_path = &output_dir.join("image.png"); assert!(copied_path.exists()); // Verify file contents are identical let original = fs::read(&file.path).unwrap(); let copied = fs::read(copied_path).unwrap(); assert_eq!(original, copied); } #[test] fn handle_gemini_copies_file() { let test_dir = setup_test_dir(); let source_dir = test_dir.join("source"); let output_dir = test_dir.join("output"); create_dir_all(&source_dir).expect("Could not create source test directory"); create_dir_all(&output_dir).expect("Could not create output test directory"); create_test_file(&source_dir.join("image.png"), "A fish playing the banjo"); let strategy = Strategy {}; let file = File { path: source_dir.join("image.png"), file_type: FileType::File, }; assert!( strategy .handle_gemini(&source_dir, &output_dir, &file) .is_ok() ); let copied_path = &output_dir.join("image.png"); assert!(copied_path.exists()); // Verify file contents are identical let original = fs::read(&file.path).unwrap(); let copied = fs::read(copied_path).unwrap(); assert_eq!(original, copied); } #[test] fn fails_if_file_is_not_part_of_source() { let test_dir = setup_test_dir(); let source_dir = test_dir.join("source"); let output_dir = test_dir.join("output"); create_dir_all(&source_dir).expect("Could not create source test directory"); create_dir_all(&output_dir).expect("Could not create output test directory"); create_test_file(&source_dir.join("image.png"), "A fish playing the banjo"); let file = File { path: output_dir.join("image.png"), file_type: FileType::File, }; assert!(Strategy::handle(&source_dir, &output_dir, &file).is_err()); } #[test] fn fails_if_destination_parent_is_not_readable() { let test_dir = setup_test_dir(); let source_dir = test_dir.join("source"); create_dir_all(&source_dir).expect("Could not create source test directory"); create_test_file(&source_dir.join("image.png"), "A fish playing the banjo"); let output_dir = Path::new(""); let file = File { path: source_dir.clone(), file_type: FileType::File, }; assert!(Strategy::handle(&source_dir, output_dir, &file).is_err()); } }