tree-type 0.4.5

Rust macros for creating type-safe filesystem tree structures
Documentation
use std::fs;
use tempfile::TempDir;
use tree_type::GenericDir;
use tree_type::GenericFile;

#[test]
fn test_generic_file_rename_success() {
    let temp_dir = TempDir::new().unwrap();
    let temp_path = temp_dir.path();

    // Create a test file
    let original_path = temp_path.join("original.txt");
    fs::write(&original_path, "test content").unwrap();

    let file = GenericFile::new(&original_path).unwrap();
    let new_path = temp_path.join("renamed.txt");

    // Test successful rename
    let renamed_file = file.rename(&new_path).unwrap();

    // Verify the file was moved
    assert!(!original_path.exists());
    assert!(new_path.exists());
    assert_eq!(fs::read_to_string(&new_path).unwrap(), "test content");

    // Verify the new instance points to the correct path
    assert_eq!(renamed_file.as_path(), new_path);
}

#[test]
fn test_generic_file_rename_with_parent_creation() {
    let temp_dir = TempDir::new().unwrap();
    let temp_path = temp_dir.path();

    // Create a test file
    let original_path = temp_path.join("original.txt");
    fs::write(&original_path, "test content").unwrap();

    let file = GenericFile::new(&original_path).unwrap();
    let new_path = temp_path.join("nested").join("deep").join("renamed.txt");

    // Test rename with parent directory creation
    let renamed_file = file.rename(&new_path).unwrap();

    // Verify the file was moved and parent directories were created
    assert!(!original_path.exists());
    assert!(new_path.exists());
    assert!(new_path.parent().unwrap().exists());
    assert_eq!(fs::read_to_string(&new_path).unwrap(), "test content");

    // Verify the new instance points to the correct path
    assert_eq!(renamed_file.as_path(), new_path);
}

#[test]
fn test_generic_file_rename_error_recovery() {
    let temp_dir = TempDir::new().unwrap();
    let temp_path = temp_dir.path();

    // Create a test file
    let original_path = temp_path.join("original.txt");
    fs::write(&original_path, "test content").unwrap();

    let file = GenericFile::new(&original_path).unwrap();

    // Try to rename to an invalid path (e.g., a directory that exists as a file)
    let blocking_file = temp_path.join("blocking");
    fs::write(&blocking_file, "blocking").unwrap();
    let invalid_path = blocking_file.join("cannot_create_here.txt");

    // Test error recovery
    match file.rename(&invalid_path) {
        Ok(_) => panic!("Expected rename to fail"),
        Err((error, recovered_file)) => {
            // Verify we got an error and the original file instance back
            assert!(
                error.kind() == std::io::ErrorKind::NotADirectory
                    || error.kind() == std::io::ErrorKind::PermissionDenied
            );
            assert_eq!(recovered_file.as_path(), original_path);

            // Verify original file still exists and is usable
            assert!(original_path.exists());
            assert_eq!(recovered_file.read_to_string().unwrap(), "test content");
        }
    }
}

#[test]
fn test_generic_dir_rename_success() {
    let temp_dir = TempDir::new().unwrap();
    let temp_path = temp_dir.path();

    // Create a test directory with content
    let original_path = temp_path.join("original_dir");
    fs::create_dir(&original_path).unwrap();
    fs::write(original_path.join("file.txt"), "content").unwrap();

    let dir = GenericDir::new(&original_path).unwrap();
    let new_path = temp_path.join("renamed_dir");

    // Test successful rename
    let renamed_dir = dir.rename(&new_path).unwrap();

    // Verify the directory was moved
    assert!(!original_path.exists());
    assert!(new_path.exists());
    assert!(new_path.join("file.txt").exists());
    assert_eq!(
        fs::read_to_string(new_path.join("file.txt")).unwrap(),
        "content"
    );

    // Verify the new instance points to the correct path
    assert_eq!(renamed_dir.as_path(), new_path);
}

#[test]
fn test_generic_dir_rename_with_parent_creation() {
    let temp_dir = TempDir::new().unwrap();
    let temp_path = temp_dir.path();

    // Create a test directory
    let original_path = temp_path.join("original_dir");
    fs::create_dir(&original_path).unwrap();
    fs::write(original_path.join("file.txt"), "content").unwrap();

    let dir = GenericDir::new(&original_path).unwrap();
    let new_path = temp_path.join("nested").join("deep").join("renamed_dir");

    // Test rename with parent directory creation
    let renamed_dir = dir.rename(&new_path).unwrap();

    // Verify the directory was moved and parent directories were created
    assert!(!original_path.exists());
    assert!(new_path.exists());
    assert!(new_path.parent().unwrap().exists());
    assert!(new_path.join("file.txt").exists());

    // Verify the new instance points to the correct path
    assert_eq!(renamed_dir.as_path(), new_path);
}

#[test]
fn test_generic_dir_rename_error_recovery() {
    let temp_dir = TempDir::new().unwrap();
    let temp_path = temp_dir.path();

    // Create a test directory
    let original_path = temp_path.join("original_dir");
    fs::create_dir(&original_path).unwrap();
    fs::write(original_path.join("file.txt"), "content").unwrap();

    let dir = GenericDir::new(&original_path).unwrap();

    // Try to rename to a path where a file already exists
    let existing_file = temp_path.join("existing_file");
    fs::write(&existing_file, "blocking").unwrap();

    // Test error recovery
    match dir.rename(&existing_file) {
        Ok(_) => panic!("Expected rename to fail"),
        Err((error, recovered_dir)) => {
            // Verify we got an error and the original directory instance back
            assert!(
                error.kind() == std::io::ErrorKind::NotADirectory
                    || error.kind() == std::io::ErrorKind::AlreadyExists
                    || error.kind() == std::io::ErrorKind::PermissionDenied
            );
            assert_eq!(recovered_dir.as_path(), original_path);

            // Verify original directory still exists and is usable
            assert!(original_path.exists());
            assert!(recovered_dir.read_dir().is_ok());
        }
    }
}

#[test]
fn test_rename_nonexistent_file() {
    let temp_dir = TempDir::new().unwrap();
    let temp_path = temp_dir.path();

    let nonexistent_path = temp_path.join("nonexistent.txt");
    let file = GenericFile::new(&nonexistent_path).unwrap();
    let new_path = temp_path.join("renamed.txt");

    // Test renaming nonexistent file
    match file.rename(&new_path) {
        Ok(_) => panic!("Expected rename to fail"),
        Err((error, recovered_file)) => {
            assert_eq!(error.kind(), std::io::ErrorKind::NotFound);
            assert_eq!(recovered_file.as_path(), nonexistent_path);
        }
    }
}

#[test]
fn test_rename_to_same_path() {
    let temp_dir = TempDir::new().unwrap();
    let temp_path = temp_dir.path();

    // Create a test file
    let file_path = temp_path.join("test.txt");
    fs::write(&file_path, "content").unwrap();

    let file = GenericFile::new(&file_path).unwrap();

    // Test renaming to the same path (should succeed as no-op)
    let renamed_file = file.rename(&file_path).unwrap();

    // Verify file still exists and content is preserved
    assert!(file_path.exists());
    assert_eq!(fs::read_to_string(&file_path).unwrap(), "content");
    assert_eq!(renamed_file.as_path(), file_path);
}