use std::fs::Metadata;
use std::path::Path;
#[cfg(unix)]
mod imp {
use super::*;
use std::os::unix::fs::MetadataExt;
pub fn file_real_size<P: AsRef<Path>>(path: P) -> std::io::Result<u64> {
Ok(path.as_ref().symlink_metadata()?.blocks() * 512)
}
pub fn file_real_size_fast<P: AsRef<Path>>(
_path: P,
metadata: &Metadata,
) -> std::io::Result<u64> {
Ok(metadata.blocks() * 512)
}
}
#[cfg(windows)]
mod imp {
use super::*;
use std::os::windows::ffi::OsStrExt;
use winapi::shared::winerror::NO_ERROR;
use winapi::um::fileapi::{GetCompressedFileSizeW, INVALID_FILE_SIZE};
pub fn file_real_size<P: AsRef<Path>>(path: P) -> std::io::Result<u64> {
let path = std::fs::canonicalize(path)?.into_os_string();
let mut pathw: Vec<u16> = Vec::with_capacity(path.len() + 1);
pathw.extend(path.encode_wide());
pathw.push(0);
let mut high: u32 = 0;
let low = unsafe { GetCompressedFileSizeW(pathw.as_ptr(), &mut high) };
if low == INVALID_FILE_SIZE {
let err = std::io::Error::last_os_error();
if err.raw_os_error().map(|e| e as u32).unwrap_or(NO_ERROR) != NO_ERROR {
return Err(err);
}
}
Ok(u64::from(high) << 32 | u64::from(low))
}
pub fn file_real_size_fast<P: AsRef<Path>>(
path: P,
_metadata: &Metadata,
) -> std::io::Result<u64> {
file_real_size(path)
}
}
#[cfg(not(any(windows, unix)))]
mod imp {
use super::*;
pub fn file_real_size<P: AsRef<Path>>(path: P) -> std::io::Result<u64> {
Ok(path.as_ref().symlink_metadata()?.len())
}
pub fn file_real_size_fast<P: AsRef<Path>>(
_path: P,
metadata: &Metadata,
) -> std::io::Result<u64> {
Ok(metadata.len())
}
}
pub fn file_real_size<P: AsRef<Path>>(path: P) -> std::io::Result<u64> {
self::imp::file_real_size(path)
}
pub fn file_real_size_fast<P: AsRef<Path>>(path: P, metadata: &Metadata) -> std::io::Result<u64> {
self::imp::file_real_size_fast(path, metadata)
}
pub trait PathExt {
fn size_on_disk(&self) -> std::io::Result<u64>;
fn size_on_disk_fast(&self, metadata: &Metadata) -> std::io::Result<u64>;
}
impl PathExt for Path {
fn size_on_disk(&self) -> std::io::Result<u64> {
file_real_size(self)
}
fn size_on_disk_fast(&self, metadata: &Metadata) -> std::io::Result<u64> {
file_real_size_fast(self, metadata)
}
}
#[test]
fn it_seems_to_work() {
let path = Path::new("Cargo.toml");
assert!(
path.size_on_disk().expect("size_on_disk")
== path
.size_on_disk_fast(&path.symlink_metadata().expect("stat"))
.expect("size_on_disk_fast")
);
}