├── .gitignore ├── .travis.yml ├── Cargo.toml ├── .github └── workflows │ └── ci.yml ├── examples └── filesize.rs ├── LICENSE.txt ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | os: 3 | - linux 4 | - windows 5 | rust: 6 | - stable 7 | - nightly 8 | cache: cargo 9 | matrix: 10 | allow_failures: 11 | - rust: nightly 12 | fast_finish: true 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "filesize" 3 | description = "Find the physical space used by a file" 4 | version = "0.2.0" 5 | authors = ["Thomas Hurst "] 6 | edition = "2018" 7 | license = "MIT" 8 | repository = "https://github.com/Freaky/rust-filesize" 9 | keywords = ["file", "compressed", "sparse", "disk", "usage"] 10 | categories = ["os"] 11 | readme = "README.md" 12 | 13 | [badges] 14 | travis-ci = { repository = "Freaky/rust-filesize" } 15 | 16 | [target."cfg(windows)".dependencies] 17 | winapi = { version = "0.3.8", features = ["fileapi", "winerror"] } 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push] 3 | jobs: 4 | check-fmt-clippy: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: hecrj/setup-rust-action@v1 8 | with: 9 | rust-version: stable 10 | components: "rustfmt,clippy" 11 | - uses: actions/checkout@v1 12 | - name: Check fmt and clippy 13 | run: cargo fmt -- --check && cargo clippy -- -Dwarnings 14 | test: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest, windows-latest, macOS-latest] 19 | rust: [stable, nightly] 20 | 21 | steps: 22 | - uses: hecrj/setup-rust-action@v1 23 | with: 24 | rust-version: ${{ matrix.rust }} 25 | - uses: actions/checkout@v1 26 | - name: Run tests 27 | run: cargo test --verbose 28 | -------------------------------------------------------------------------------- /examples/filesize.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::Path; 3 | 4 | use filesize::PathExt; 5 | 6 | fn display_path>(path: P) -> io::Result<()> { 7 | let path = path.as_ref(); 8 | let meta = path.symlink_metadata()?; 9 | let logical = meta.len(); 10 | let physical = path.size_on_disk_fast(&meta)?; 11 | 12 | println!( 13 | "{:>9} {:>9} {:>9.2}x {}", 14 | logical, 15 | physical, 16 | physical as f64 / logical as f64, 17 | path.display() 18 | ); 19 | 20 | Ok(()) 21 | } 22 | 23 | fn main() -> io::Result<()> { 24 | println!("{:>9} {:>9} {:>9} Path", "Logical", "Physical", "Ratio"); 25 | 26 | for path in std::env::args_os().skip(1) { 27 | if let Err(e) = display_path(&path) { 28 | eprintln!("{}: {}", path.to_string_lossy(), e); 29 | } 30 | } 31 | 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-2020 Thomas Hurst 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Cargo](https://img.shields.io/crates/v/filesize.svg)][crate] 2 | [![Documentation](https://docs.rs/filesize/badge.svg)][docs] 3 | [![Build Status](https://travis-ci.org/Freaky/rust-filesize.svg?branch=master)](https://travis-ci.org/Freaky/rust-filesize) 4 | [![CI](https://github.com/Freaky/rust-filesize/workflows/build/badge.svg)][ci] 5 | 6 | # filesize 7 | 8 | Cross-platform physical disk space use retrieval for Rust. 9 | 10 | ## Synopsis 11 | 12 | ```rust 13 | pub trait PathExt { 14 | fn size_on_disk(&self) -> std::io::Result; 15 | fn size_on_disk_fast(&self, metadata: &Metadata) -> std::io::Result; 16 | } 17 | impl PathExt for std::path::Path; 18 | 19 | pub fn file_real_size>(path: P) -> std::io::Result; 20 | pub fn file_real_size_fast>( 21 | path: P, 22 | metadata: &Metadata 23 | ) -> std::io::Result; 24 | ``` 25 | 26 | ## Description 27 | 28 | `filesize` abstracts platform-specific methods of determining the real space used 29 | by files, taking into account filesystem compression and sparse files. 30 | 31 | It provides two standalone functions, `file_real_size`, and `file_real_size_fast`, 32 | and as of version 0.2, a `std::path::Path` extension trait offering identical 33 | functions named `size_on_disk` and `size_on_disk_fast`. 34 | 35 | The `_fast` variants accept a `std::fs::Metadata` reference which will be used 36 | to cheaply calculate the size on disk if the platform supports that. This is 37 | intended for cases such as directory traversal, where metadata is available 38 | anyway, and where metadata is needed for other purposes. 39 | 40 | ## Example 41 | 42 | ```rust 43 | use std::path::Path; 44 | use filesize::PathExt; 45 | 46 | let path = Path::new("Cargo.toml"); 47 | let metadata = path.symlink_metadata()?; 48 | 49 | let realsize = path.size_on_disk()?; 50 | let realsize = path.size_on_disk_fast(&metadata)?; 51 | 52 | // Older interface 53 | use filesize::{file_real_size, file_real_size_fast}; 54 | 55 | let realsize = file_real_size(path)?; 56 | let realsize = file_real_size_fast(path, &metadata)?; 57 | ``` 58 | 59 | ## Platform-specific Behaviour 60 | 61 | On Unix platforms this is a thin wrapper around [`std::fs::symlink_metadata()`] 62 | and [`std::os::unix::fs::MetadataExt`], simply returning `blocks() * 512`. The 63 | `_fast` functions disregard the file path entirely and use the passed metadata 64 | directly. 65 | 66 | On Windows, it wraps [`GetCompressedFileSizeW()`], and the `_fast` functions 67 | disregard the passed metadata entirely. 68 | 69 | On any other platforms, it wraps [`std::fs::symlink_metadata()`] and only returns 70 | `len()`, while the `_fast` variants also disregard the path and use the passed 71 | metadata directly. 72 | 73 | 74 | [`GetCompressedFileSizeW()`]: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getcompressedfilesizew 75 | [`std::fs::symlink_metadata()`]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html 76 | [`std::os::unix::fs::MetadataExt`]: https://doc.rust-lang.org/std/os/unix/fs/trait.MetadataExt.html 77 | [crate]: https://crates.io/crates/filesize 78 | [docs]: https://docs.rs/filesize 79 | [ci]: https://github.com/Freaky/rust-filesize/actions?query=workflow%3Abuild 80 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! `filesize` abstracts platform-specific methods of determining the real space used 3 | //! by files, taking into account filesystem compression and sparse files. 4 | //! 5 | //! It provides two standalone functions, `file_real_size`, and `file_real_size_fast`, 6 | //! and as of version 0.2, a `std::path::Path` extension trait offering identical 7 | //! functions named `size_on_disk` and `size_on_disk_fast`. 8 | //! 9 | //! The `_fast` variants accept a `std::fs::Metadata` reference which will be used 10 | //! to cheaply calculate the size on disk if the platform supports that. This is 11 | //! intended for cases such as directory traversal, where metadata is available 12 | //! anyway, and where metadata is needed for other purposes. 13 | //! 14 | //! ## Example 15 | //! 16 | //! ```rust 17 | //! use std::path::Path; 18 | //! use filesize::PathExt; 19 | //! 20 | //! # fn main() -> std::io::Result<()> { 21 | //! let path = Path::new("Cargo.toml"); 22 | //! let metadata = path.symlink_metadata()?; 23 | //! 24 | //! let realsize = path.size_on_disk()?; 25 | //! let realsize = path.size_on_disk_fast(&metadata)?; 26 | //! 27 | //! // Older interface 28 | //! use filesize::{file_real_size, file_real_size_fast}; 29 | //! 30 | //! let realsize = file_real_size(path)?; 31 | //! let realsize = file_real_size_fast(path, &metadata)?; 32 | //! # Ok(()) 33 | //! # } 34 | //! ``` 35 | //! 36 | //! ## Platform-specific Behaviour 37 | //! 38 | //! On Unix platforms this is a thin wrapper around [`std::fs::symlink_metadata()`] 39 | //! and [`std::os::unix::fs::MetadataExt`], simply returning `blocks() * 512`. The 40 | //! `_fast` functions disregard the file path entirely and use the passed metadata 41 | //! directly. 42 | //! 43 | //! On Windows, it wraps [`GetCompressedFileSizeW()`], and the `_fast` functions 44 | //! disregard the passed metadata entirely. 45 | //! 46 | //! On any other platforms, it wraps [`std::fs::symlink_metadata()`] and only returns 47 | //! `len()`, while the `_fast` variants also disregard the path and use the passed 48 | //! metadata directly. 49 | //! 50 | //! 51 | //! [`GetCompressedFileSizeW()`]: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getcompressedfilesizew 52 | //! [`std::fs::symlink_metadata()`]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html 53 | //! [`std::os::unix::fs::MetadataExt`]: https://doc.rust-lang.org/std/os/unix/fs/trait.MetadataExt.html 54 | 55 | use std::fs::Metadata; 56 | use std::path::Path; 57 | 58 | #[cfg(unix)] 59 | mod imp { 60 | use super::*; 61 | 62 | use std::os::unix::fs::MetadataExt; 63 | 64 | pub fn file_real_size>(path: P) -> std::io::Result { 65 | Ok(path.as_ref().symlink_metadata()?.blocks() * 512) 66 | } 67 | 68 | pub fn file_real_size_fast>( 69 | _path: P, 70 | metadata: &Metadata, 71 | ) -> std::io::Result { 72 | Ok(metadata.blocks() * 512) 73 | } 74 | } 75 | 76 | #[cfg(windows)] 77 | mod imp { 78 | use super::*; 79 | 80 | use std::os::windows::ffi::OsStrExt; 81 | 82 | use winapi::shared::winerror::NO_ERROR; 83 | use winapi::um::fileapi::{GetCompressedFileSizeW, INVALID_FILE_SIZE}; 84 | 85 | pub fn file_real_size>(path: P) -> std::io::Result { 86 | let path = std::fs::canonicalize(path)?.into_os_string(); 87 | let mut pathw: Vec = Vec::with_capacity(path.len() + 1); 88 | pathw.extend(path.encode_wide()); 89 | pathw.push(0); 90 | 91 | let mut high: u32 = 0; 92 | let low = unsafe { GetCompressedFileSizeW(pathw.as_ptr(), &mut high) }; 93 | 94 | if low == INVALID_FILE_SIZE { 95 | let err = std::io::Error::last_os_error(); 96 | if err.raw_os_error().map(|e| e as u32).unwrap_or(NO_ERROR) != NO_ERROR { 97 | return Err(err); 98 | } 99 | } 100 | 101 | Ok(u64::from(high) << 32 | u64::from(low)) 102 | } 103 | 104 | pub fn file_real_size_fast>( 105 | path: P, 106 | _metadata: &Metadata, 107 | ) -> std::io::Result { 108 | file_real_size(path) 109 | } 110 | } 111 | 112 | #[cfg(not(any(windows, unix)))] 113 | mod imp { 114 | use super::*; 115 | 116 | pub fn file_real_size>(path: P) -> std::io::Result { 117 | Ok(path.as_ref().symlink_metadata()?.len()) 118 | } 119 | 120 | pub fn file_real_size_fast>( 121 | _path: P, 122 | metadata: &Metadata, 123 | ) -> std::io::Result { 124 | Ok(metadata.len()) 125 | } 126 | } 127 | 128 | /// Get the on-disk size of the file at the given `path`. 129 | /// 130 | /// ```rust 131 | /// # fn main() -> std::io::Result<()> { 132 | /// let realsize = filesize::file_real_size("Cargo.toml")?; 133 | /// # Ok(()) 134 | /// # } 135 | /// ``` 136 | pub fn file_real_size>(path: P) -> std::io::Result { 137 | self::imp::file_real_size(path) 138 | } 139 | 140 | /// Get the on-disk size of the file at the given `path`, using the provided 141 | /// `std::fs::Metadata` instance if possible. 142 | /// 143 | /// This should normally only be used when metadata is cheaply available, 144 | /// for instance, during a directory traversal, or when metadata will be used 145 | /// for other purposes. 146 | /// 147 | /// ```rust 148 | /// # fn main() -> std::io::Result<()> { 149 | /// let realsize = filesize::file_real_size_fast( 150 | /// "Cargo.toml", 151 | /// &std::fs::symlink_metadata("Cargo.toml")? 152 | /// )?; 153 | /// # Ok(()) 154 | /// # } 155 | /// ``` 156 | pub fn file_real_size_fast>(path: P, metadata: &Metadata) -> std::io::Result { 157 | self::imp::file_real_size_fast(path, metadata) 158 | } 159 | 160 | /// An extension trait for `std::path::Path` to retrieve the on-disk size of a 161 | /// given file. 162 | pub trait PathExt { 163 | /// Get the on-disk size of the file at the given `Path`. 164 | /// 165 | /// ```rust 166 | /// use std::path::Path; 167 | /// use filesize::PathExt; 168 | /// 169 | /// # fn main() -> std::io::Result<()> { 170 | /// let realsize = Path::new("Cargo.toml").size_on_disk()?; 171 | /// # Ok(()) 172 | /// # } 173 | /// ``` 174 | fn size_on_disk(&self) -> std::io::Result; 175 | 176 | /// Get the on-disk size of the file at the given `Path`, using the provided 177 | /// `std::fs::Metadata` instance if possible. 178 | /// 179 | /// This should normally only be used when metadata is cheaply available, 180 | /// for instance, during a directory traversal, or when metadata will be used 181 | /// for other purposes. 182 | /// 183 | /// ```rust 184 | /// use std::path::Path; 185 | /// use filesize::PathExt; 186 | /// 187 | /// # fn main() -> std::io::Result<()> { 188 | /// let path = Path::new("Cargo.toml"); 189 | /// let metadata = path.symlink_metadata()?; 190 | /// let realsize = path.size_on_disk_fast(&metadata)?; 191 | /// # Ok(()) 192 | /// # } 193 | /// ``` 194 | fn size_on_disk_fast(&self, metadata: &Metadata) -> std::io::Result; 195 | } 196 | 197 | impl PathExt for Path { 198 | fn size_on_disk(&self) -> std::io::Result { 199 | file_real_size(self) 200 | } 201 | 202 | fn size_on_disk_fast(&self, metadata: &Metadata) -> std::io::Result { 203 | file_real_size_fast(self, metadata) 204 | } 205 | } 206 | 207 | #[test] 208 | fn it_seems_to_work() { 209 | let path = Path::new("Cargo.toml"); 210 | assert!( 211 | path.size_on_disk().expect("size_on_disk") 212 | == path 213 | .size_on_disk_fast(&path.symlink_metadata().expect("stat")) 214 | .expect("size_on_disk_fast") 215 | ); 216 | } 217 | --------------------------------------------------------------------------------