#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, allow(unused_attributes))]
#![allow(unexpected_cfgs, unstable_name_collisions, unused_macros)]
#[cfg(windows)]
extern crate windows_sys;
macro_rules! cfg_async_std {
($($item:item)*) => {
$(
#[cfg(feature = "async-std")]
#[cfg_attr(docsrs, doc(cfg(feature = "async-std")))]
$item
)*
}
}
macro_rules! cfg_fs_err2 {
($($item:item)*) => {
$(
#[cfg(feature = "fs-err2")]
#[cfg_attr(docsrs, doc(cfg(feature = "fs-err2")))]
$item
)*
}
}
macro_rules! cfg_fs_err2_tokio {
($($item:item)*) => {
$(
#[cfg(feature = "fs-err2-tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "fs-err2-tokio")))]
$item
)*
}
}
macro_rules! cfg_fs_err3 {
($($item:item)*) => {
$(
#[cfg(feature = "fs-err3")]
#[cfg_attr(docsrs, doc(cfg(feature = "fs-err3")))]
$item
)*
}
}
macro_rules! cfg_fs_err3_tokio {
($($item:item)*) => {
$(
#[cfg(feature = "fs-err3-tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "fs-err3-tokio")))]
$item
)*
}
}
macro_rules! cfg_smol {
($($item:item)*) => {
$(
#[cfg(feature = "smol")]
#[cfg_attr(docsrs, doc(cfg(feature = "smol")))]
$item
)*
}
}
macro_rules! cfg_tokio {
($($item:item)*) => {
$(
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
$item
)*
}
}
macro_rules! cfg_sync {
($($item:item)*) => {
$(
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
$item
)*
}
}
macro_rules! cfg_async {
($($item:item)*) => {
$(
#[cfg(any(
feature = "smol",
feature = "async-std",
feature = "tokio",
feature = "fs-err2-tokio",
feature = "fs-err3-tokio",
))]
#[cfg_attr(docsrs, doc(cfg(any(
feature = "smol",
feature = "async-std",
feature = "tokio",
feature = "fs-err2-tokio",
feature = "fs-err3-tokio",
))))]
$item
)*
}
}
#[cfg(unix)]
mod unix;
#[cfg(unix)]
use unix as sys;
#[cfg(windows)]
mod windows;
#[cfg(windows)]
use windows as sys;
#[cfg(any(unix, windows))]
mod file_ext;
#[cfg(all(feature = "fs-err2", any(unix, windows)))]
#[cfg_attr(docsrs, doc(cfg(feature = "fs-err2")))]
pub mod fs_err2 {
pub use crate::FileExt;
}
#[cfg(all(feature = "fs-err3", any(unix, windows)))]
#[cfg_attr(docsrs, doc(cfg(feature = "fs-err3")))]
pub mod fs_err3 {
pub use crate::FileExt;
}
#[cfg(all(feature = "async-std", any(unix, windows)))]
#[cfg_attr(docsrs, doc(cfg(feature = "async-std")))]
pub mod async_std {
pub use crate::{AsyncFileExt, DynAsyncFileExt};
}
#[cfg(all(feature = "fs-err2-tokio", any(unix, windows)))]
#[cfg_attr(docsrs, doc(cfg(feature = "fs-err2-tokio")))]
pub mod fs_err2_tokio {
pub use crate::{AsyncFileExt, DynAsyncFileExt};
}
#[cfg(all(feature = "fs-err3-tokio", any(unix, windows)))]
#[cfg_attr(docsrs, doc(cfg(feature = "fs-err3-tokio")))]
pub mod fs_err3_tokio {
pub use crate::{AsyncFileExt, DynAsyncFileExt};
}
#[cfg(all(feature = "smol", any(unix, windows)))]
#[cfg_attr(docsrs, doc(cfg(feature = "smol")))]
pub mod smol {
pub use crate::{AsyncFileExt, DynAsyncFileExt};
}
#[cfg(all(feature = "tokio", any(unix, windows)))]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
pub mod tokio {
pub use crate::{AsyncFileExt, DynAsyncFileExt};
}
mod fs_stats;
pub use fs_stats::FsStats;
mod try_lock_error;
pub use try_lock_error::TryLockError;
use std::io::Result;
#[cfg(any(unix, windows))]
use std::path::Path;
#[cfg(any(unix, windows))]
pub fn statvfs<P>(path: P) -> Result<FsStats>
where
P: AsRef<Path>,
{
sys::statvfs(path.as_ref())
}
#[cfg(any(unix, windows))]
pub fn free_space<P>(path: P) -> Result<u64>
where
P: AsRef<Path>,
{
statvfs(path).map(|stat| stat.free_space)
}
#[cfg(any(unix, windows))]
pub fn available_space<P>(path: P) -> Result<u64>
where
P: AsRef<Path>,
{
statvfs(path).map(|stat| stat.available_space)
}
#[cfg(any(unix, windows))]
pub fn total_space<P>(path: P) -> Result<u64>
where
P: AsRef<Path>,
{
statvfs(path).map(|stat| stat.total_space)
}
#[cfg(any(unix, windows))]
pub fn allocation_granularity<P>(path: P) -> Result<u64>
where
P: AsRef<Path>,
{
statvfs(path).map(|stat| stat.allocation_granularity)
}
mod sealed {
pub trait Sealed {}
impl<F: Sealed + ?Sized> Sealed for &F {}
}
pub trait FileExt: sealed::Sealed {
fn allocated_size(&self) -> Result<u64>;
fn allocate(&self, len: u64) -> Result<()>;
fn lock_shared(&self) -> Result<()>;
fn lock(&self) -> Result<()>;
fn try_lock_shared(&self) -> std::result::Result<(), TryLockError>;
fn try_lock(&self) -> std::result::Result<(), TryLockError>;
fn unlock(&self) -> Result<()>;
}
impl<F: FileExt + ?Sized> FileExt for &F {
#[cfg_attr(not(tarpaulin), inline(always))]
fn allocated_size(&self) -> Result<u64> {
<F as FileExt>::allocated_size(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn allocate(&self, len: u64) -> Result<()> {
<F as FileExt>::allocate(*self, len)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn lock_shared(&self) -> Result<()> {
<F as FileExt>::lock_shared(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn lock(&self) -> Result<()> {
<F as FileExt>::lock(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn try_lock_shared(&self) -> std::result::Result<(), TryLockError> {
<F as FileExt>::try_lock_shared(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn try_lock(&self) -> std::result::Result<(), TryLockError> {
<F as FileExt>::try_lock(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn unlock(&self) -> Result<()> {
<F as FileExt>::unlock(*self)
}
}
pub trait AsyncFileExt: sealed::Sealed {
fn allocated_size(&self) -> impl core::future::Future<Output = Result<u64>>;
fn allocate(&self, len: u64) -> impl core::future::Future<Output = Result<()>>;
fn lock_shared(&self) -> Result<()>;
fn lock(&self) -> Result<()>;
fn try_lock_shared(&self) -> std::result::Result<(), crate::TryLockError>;
fn try_lock(&self) -> std::result::Result<(), crate::TryLockError>;
fn unlock(&self) -> Result<()>;
fn unlock_async(&self) -> impl core::future::Future<Output = Result<()>>;
}
impl<F: AsyncFileExt + ?Sized> AsyncFileExt for &F {
#[cfg_attr(not(tarpaulin), inline(always))]
async fn allocated_size(&self) -> Result<u64> {
<F as AsyncFileExt>::allocated_size(*self).await
}
#[cfg_attr(not(tarpaulin), inline(always))]
async fn allocate(&self, len: u64) -> Result<()> {
<F as AsyncFileExt>::allocate(*self, len).await
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn lock_shared(&self) -> Result<()> {
<F as AsyncFileExt>::lock_shared(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn lock(&self) -> Result<()> {
<F as AsyncFileExt>::lock(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn try_lock_shared(&self) -> std::result::Result<(), crate::TryLockError> {
<F as AsyncFileExt>::try_lock_shared(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn try_lock(&self) -> std::result::Result<(), crate::TryLockError> {
<F as AsyncFileExt>::try_lock(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn unlock(&self) -> Result<()> {
<F as AsyncFileExt>::unlock(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
async fn unlock_async(&self) -> Result<()> {
<F as AsyncFileExt>::unlock_async(*self).await
}
}
pub type BoxFuture<'a, T> = core::pin::Pin<Box<dyn core::future::Future<Output = T> + Send + 'a>>;
pub trait DynAsyncFileExt: sealed::Sealed {
fn allocated_size(&self) -> BoxFuture<'_, Result<u64>>;
fn allocate(&self, len: u64) -> BoxFuture<'_, Result<()>>;
fn lock_shared(&self) -> Result<()>;
fn lock(&self) -> Result<()>;
fn try_lock_shared(&self) -> std::result::Result<(), crate::TryLockError>;
fn try_lock(&self) -> std::result::Result<(), crate::TryLockError>;
fn unlock(&self) -> Result<()>;
fn unlock_async(&self) -> BoxFuture<'_, Result<()>>;
}
impl<F: DynAsyncFileExt + ?Sized> DynAsyncFileExt for &F {
#[cfg_attr(not(tarpaulin), inline(always))]
fn allocated_size(&self) -> BoxFuture<'_, Result<u64>> {
<F as DynAsyncFileExt>::allocated_size(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn allocate(&self, len: u64) -> BoxFuture<'_, Result<()>> {
<F as DynAsyncFileExt>::allocate(*self, len)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn lock_shared(&self) -> Result<()> {
<F as DynAsyncFileExt>::lock_shared(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn lock(&self) -> Result<()> {
<F as DynAsyncFileExt>::lock(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn try_lock_shared(&self) -> std::result::Result<(), crate::TryLockError> {
<F as DynAsyncFileExt>::try_lock_shared(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn try_lock(&self) -> std::result::Result<(), crate::TryLockError> {
<F as DynAsyncFileExt>::try_lock(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn unlock(&self) -> Result<()> {
<F as DynAsyncFileExt>::unlock(*self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn unlock_async(&self) -> BoxFuture<'_, Result<()>> {
<F as DynAsyncFileExt>::unlock_async(*self)
}
}
#[cfg(all(test, any(unix, windows)))]
mod tests {
extern crate tempfile;
use super::*;
fn tempdir() -> tempfile::TempDir {
tempfile::TempDir::with_prefix("fs4").unwrap()
}
#[test]
fn free_space_returns_ok() {
let dir = tempdir();
let free = free_space(dir.path()).unwrap();
let total = total_space(dir.path()).unwrap();
assert!(
free <= total,
"free_space ({free}) must not exceed total_space ({total})",
);
}
#[test]
fn available_space_returns_ok() {
let dir = tempdir();
let available = available_space(dir.path()).unwrap();
let total = total_space(dir.path()).unwrap();
assert!(
available <= total,
"available_space ({available}) must not exceed total_space ({total})",
);
}
#[test]
fn total_space_is_non_zero() {
let dir = tempdir();
assert!(
total_space(dir.path()).unwrap() > 0,
"total_space on a tempdir's volume should be non-zero",
);
}
#[cfg(unix)]
#[test]
fn missing_path_errors() {
let dir = tempdir();
let missing = dir.path().join("definitely-does-not-exist");
assert!(free_space(&missing).is_err());
assert!(available_space(&missing).is_err());
assert!(total_space(&missing).is_err());
}
}