use std::{fs, io, path::Path};
pub fn file_eq(path1: &Path, path2: &Path) -> io::Result<bool> {
let path1 = &path1.canonicalize()?;
let path2 = &path2.canonicalize()?;
if path1 == path2 {
return Ok(true);
}
if fs::metadata(path1)?.len() != fs::metadata(path2)?.len() {
return Ok(false);
}
Ok(fs::read(path1)? == fs::read(path2)?)
}
#[cfg(test)]
mod tests {
use super::*;
use assert_fs::prelude::{FileWriteStr, PathChild, SymlinkToFile};
use std::{io, path::Path};
#[test]
fn files_with_same_canonicalized_paths_returns_true() {
let dir = assert_fs::TempDir::new().unwrap();
let file1 = dir.child("file1");
let file1_alt = {
let mut f = dir.to_path_buf();
f.push("..");
f.push(dir.file_name().unwrap());
f.push(file1.file_name().unwrap());
f
};
file1.write_str("abc").unwrap();
let actual = file_eq(&file1, &file1_alt);
assert!(actual.unwrap());
dir.close().unwrap();
}
#[test]
fn symlinks_leading_to_same_file_returns_true() {
let dir = assert_fs::TempDir::new().unwrap();
let file = dir.child("file");
let symlink1 = dir.child("symlink1");
let symlink2a = dir.child("symlink2a");
let symlink2b = dir.child("symlink2b");
file.write_str("abc").unwrap();
symlink1.symlink_to_file(&file).unwrap();
symlink2a.symlink_to_file(&file).unwrap();
symlink2b.symlink_to_file(&symlink2a).unwrap();
let actual = file_eq(&symlink1, &symlink2b);
assert!(actual.unwrap());
dir.close().unwrap();
}
#[test]
fn symlinks_leading_to_same_directory_returns_true() {
let dir = assert_fs::TempDir::new().unwrap();
let subdir = dir.child("subdir");
let symlink1 = dir.child("symlink1");
let symlink2a = dir.child("symlink2a");
let symlink2b = dir.child("symlink2b");
subdir.write_str("abc").unwrap();
symlink1.symlink_to_file(&subdir).unwrap();
symlink2a.symlink_to_file(&subdir).unwrap();
symlink2b.symlink_to_file(&symlink2a).unwrap();
let actual = file_eq(&symlink1, &symlink2b);
assert!(actual.unwrap());
dir.close().unwrap();
}
#[test]
fn files_with_same_content_returns_true() {
let dir = assert_fs::TempDir::new().unwrap();
let file1 = dir.child("file1");
let file2 = dir.child("file2");
file1.write_str("abc").unwrap();
file2.write_str("abc").unwrap();
let actual = file_eq(&file1, &file2);
assert!(actual.unwrap());
dir.close().unwrap();
}
#[test]
fn files_of_different_size_returns_false() {
let dir = assert_fs::TempDir::new().unwrap();
let file1 = dir.child("file1");
let file2 = dir.child("file2");
file1.write_str("abc").unwrap();
file2.write_str("abcd").unwrap();
let actual = file_eq(&file1, &file2);
assert!(!actual.unwrap());
dir.close().unwrap();
}
#[test]
fn files_of_same_size_but_different_content_returns_false() {
let dir = assert_fs::TempDir::new().unwrap();
let file1 = dir.child("file1");
let file2 = dir.child("file2");
file1.write_str("abc").unwrap();
file2.write_str("xyz").unwrap();
let actual = file_eq(&file1, &file2);
assert!(!actual.unwrap());
dir.close().unwrap();
}
#[test]
fn bad_path_returns_path_not_found() {
let dir = assert_fs::TempDir::new().unwrap();
let file1 = dir.child("file1");
file1.write_str("abc").unwrap();
let actual = file_eq(&file1, Path::new("no-such-file"));
assert_eq!(actual.unwrap_err().kind(), io::ErrorKind::NotFound);
}
}