Skip to main content

compare_dir/
lib.rs

1mod dir_comparer;
2mod file_comparer;
3mod file_hash_cache;
4mod file_hasher;
5mod file_iterator;
6mod progress_reporter;
7
8pub use dir_comparer::{DirectoryComparer, FileComparisonMethod};
9pub use file_comparer::{Classification, FileComparer, FileComparisonResult};
10pub(crate) use file_hash_cache::FileHashCache;
11pub use file_hasher::{DuplicatedFiles, FileHasher};
12pub(crate) use file_iterator::FileIterator;
13pub(crate) use progress_reporter::ProgressReporter;
14
15use std::path::{Path, StripPrefixError};
16
17pub(crate) fn human_readable_size(size: u64) -> String {
18    const MB: u64 = 1024 * 1024;
19    const GB: u64 = 1024 * 1024 * 1024;
20    if size >= GB {
21        format!("{:.1}GB", size as f64 / GB as f64)
22    } else if size >= MB {
23        format!("{:.1}MB", size as f64 / MB as f64)
24    } else {
25        format!("{} bytes", size)
26    }
27}
28
29/// Workaround for https://github.com/kojiishi/compare-dir/issues/8
30pub(crate) fn strip_prefix<'a>(path: &'a Path, base: &Path) -> Result<&'a Path, StripPrefixError> {
31    let result = path.strip_prefix(base);
32    #[cfg(windows)]
33    if let Ok(result_path) = result {
34        let result_os_str = result_path.as_os_str();
35        let result_bytes = result_os_str.as_encoded_bytes();
36        if !result_bytes.is_empty() && result_bytes[0] as char == std::path::MAIN_SEPARATOR {
37            // TODO: Use `slice_encoded_bytes` once stabilized.
38            // https://github.com/rust-lang/rust/issues/118485
39            return Ok(Path::new(unsafe {
40                use std::ffi::OsStr;
41                OsStr::from_encoded_bytes_unchecked(&result_bytes[1..])
42            }));
43        }
44    }
45    result
46}
47
48#[cfg(test)]
49mod tests {
50    #[cfg(windows)]
51    use super::*;
52
53    #[cfg(windows)]
54    #[test]
55    fn test_strip_prefix_share_root() -> anyhow::Result<()> {
56        let path = Path::new(r"\\server\share\dir1\dir2");
57        let base = Path::new(r"\\server\share");
58        assert_eq!(strip_prefix(path, base)?.to_str().unwrap(), r"dir1\dir2");
59        assert_eq!(path.strip_prefix(base)?.to_str().unwrap(), r"dir1\dir2");
60        Ok(())
61    }
62
63    #[cfg(windows)]
64    #[test]
65    fn test_strip_prefix_unc_root() -> anyhow::Result<()> {
66        let path = Path::new(r"\\?\UNC\server\share\dir1\dir2");
67        let base = Path::new(r"\\?\UNC\server\share");
68        assert_eq!(strip_prefix(path, base)?.to_str().unwrap(), r"dir1\dir2");
69        // assert_eq!(path.strip_prefix(base)?.to_str().unwrap(), r"dir1\dir2");
70        Ok(())
71    }
72}