hoard 0.6.1

Hoard backups of files across your filesystem into one location.
Documentation
use std::fs::Metadata;
use std::fs::Permissions;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use tokio::fs::File;

use hoard::checksum::Checksum;
use hoard::hoard_item::HoardItem;
use tempfile::{NamedTempFile, TempDir};
use tokio::io::{AsyncReadExt, AsyncSeekExt, SeekFrom};

pub fn get_temp_file() -> NamedTempFile {
    NamedTempFile::new().expect("failed to create temp file")
}

pub fn get_temp_dir() -> TempDir {
    TempDir::new().expect("failed to create temp dir")
}

async fn get_metadata(left: &File, right: &File) -> (Metadata, Metadata) {
    let left_meta = left
        .metadata()
        .await
        .expect("failed to get metadata for left file");
    let right_meta = right
        .metadata()
        .await
        .expect("failed to get metadata for right file");

    (left_meta, right_meta)
}

pub async fn assert_eq_files(left: &mut File, right: &mut File) {
    assert_eq_file_types(left, right).await;
    assert_eq_file_permissions(left, right).await;
    assert_eq_file_contents(left, right).await;
}

pub async fn assert_eq_file_types(left: &File, right: &File) {
    let (left_meta, right_meta) = get_metadata(left, right).await;
    assert_eq!(
        left_meta.file_type(),
        right_meta.file_type(),
        "files are not the samee type (dir, file, symlink)"
    );
}

pub async fn assert_eq_file_contents(left: &mut File, right: &mut File) {
    let (left_meta, right_meta) = get_metadata(left, right).await;
    assert_eq!(
        left_meta.len(),
        right_meta.len(),
        "files are not the same length"
    );

    // Ensure seek to beginning of file
    left.seek(SeekFrom::Start(0))
        .await
        .expect("failed to seek to beginning of left file (beginning)");
    right
        .seek(SeekFrom::Start(0))
        .await
        .expect("failed to seek to beginning of right file (beginning)");

    // Create iterator over bytes
    let (mut left_bytes, mut right_bytes) = (Vec::new(), Vec::new());
    left.read_to_end(&mut left_bytes).await;
    right.read_to_end(&mut right_bytes).await;
    assert_eq!(left_bytes, right_bytes, "file contents differ");

    // Return to beginning of file before returning
    left.seek(SeekFrom::Start(0))
        .await
        .expect("failed to seek to beginning of left file (end)");
    right
        .seek(SeekFrom::Start(0))
        .await
        .expect("failed to seek to beginning of right file (end)");
}

#[cfg(not(unix))]
fn assert_mode(left_perm: &Permissions, right_perm: &Permissions) {}

#[cfg(unix)]
fn assert_mode(left_perm: &Permissions, right_perm: &Permissions) {
    assert_eq!(
        left_perm.mode(),
        right_perm.mode(),
        "Unix file modes differ"
    );
}

pub async fn assert_eq_file_permissions(left: &File, right: &File) {
    let (left_meta, right_meta) = get_metadata(left, right).await;

    let left_perm = left_meta.permissions();
    let right_perm = right_meta.permissions();

    // The only permission currently available on all systems
    assert_eq!(
        left_perm.readonly(),
        right_perm.readonly(),
        "exactly one of the files is readonly"
    );

    assert_mode(&left_perm, &right_perm);
}