mod magic;
use crate::magic::which_kind;
use std::{fmt, path::Path};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub enum FsKind {
Fat32,
ExFat,
Apfs,
Ext2,
Hfs,
Btrfs,
Bcachefs,
Xfs,
Fuse,
Ntfs,
F2fs,
#[default]
Unknown,
}
impl fmt::Display for FsKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FsKind::Fat32 => write!(f, "FAT32"),
FsKind::ExFat => write!(f, "exFAT"),
FsKind::Apfs => write!(f, "APFS"),
FsKind::Ext2 => write!(f, "ext2/3/4"),
FsKind::Hfs => write!(f, "HFS+"),
FsKind::Btrfs => write!(f, "BTRFS"),
FsKind::Bcachefs => write!(f, "bcachefs"),
FsKind::Xfs => write!(f, "XFS"),
FsKind::Fuse => write!(f, "FUSE"),
FsKind::Ntfs => write!(f, "NTFS"),
FsKind::F2fs => write!(f, "F2FS"),
FsKind::Unknown => write!(f, "Unknown"),
}
}
}
impl FsKind {
#[cfg(unix)]
pub fn try_from_path(path: impl AsRef<Path>) -> rustix::io::Result<FsKind> {
let stat = rustix::fs::statfs(path.as_ref())?;
#[cfg(target_os = "linux")]
let data = stat.f_type;
#[cfg(target_os = "macos")]
let data = stat.f_fstypename;
let kind = which_kind(data);
Ok(kind)
}
#[cfg(windows)]
pub fn try_from_path(path: impl AsRef<Path>) -> windows::core::Result<FsKind> {
use std::os::windows::ffi::OsStrExt;
use windows::Win32::Foundation::MAX_PATH;
use windows::Win32::Storage::FileSystem::{GetVolumeInformationW, GetVolumePathNameW};
use windows::core::PCWSTR;
let path_wide: Vec<u16> = path
.as_ref()
.as_os_str()
.encode_wide()
.chain(std::iter::once(0))
.collect();
let mut volume_path = [0u16; MAX_PATH as usize + 1];
unsafe {
GetVolumePathNameW(PCWSTR(path_wide.as_ptr()), &mut volume_path)?;
}
let mut fs_name_buffer = [0u16; 16];
unsafe {
GetVolumeInformationW(
PCWSTR(volume_path.as_ptr()),
None,
None,
None,
None,
Some(&mut fs_name_buffer),
)?;
}
let kind = which_kind(fs_name_buffer);
Ok(kind)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect() {
let path = Path::new(".");
let kind = FsKind::try_from_path(path).unwrap();
assert_ne!(kind, FsKind::Unknown);
}
}