filesystem_kind/
lib.rs

1#![warn(clippy::pedantic)]
2#![warn(clippy::nursery)]
3use std::fs::File;
4
5/// The kind of filesystem that a file resides on.
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub enum FileSystemKind {
8    /// A recognized filesystem.
9    Recognized(FileSystemName),
10    /// An unrecognized filesystem.
11    Unrecognized(Box<String>),
12    // todo: is there a better way to keep this pointer-sized? `Box<str>` is 2 pointer sizes :(
13    // todo: should this be `Option<Box<_>>`?
14    // todo: what order should enum variants be in?
15    // todo: add `Unsupported` variant for OS's where
16    //   this information can't be easily queried?
17}
18
19/// An enum representing all recognized filesystem names.
20#[non_exhaustive]
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22#[repr(u16)]
23// todo: come up with naming conventions,
24//   i.e. should they all end in "fs"?
25//   Or should we stick to the common renderings of each fs?
26pub enum FileSystemName {
27    Autofs,
28    Btrfs,
29    Cdfs,
30    Devfs,
31    Ext2,
32    Ext3,
33    Ext4,
34    Fat,
35    Fat32,
36    Fatx,
37    Fdescfs,
38    Fusefs,
39    Linprocfs, // todo: should this just be procfs?
40    Linsysfs, // todo: should this just be sysfs?
41    Mqueuefs,
42    Nfs,
43    Ntfs,
44    Nullfs,
45    Procfs,
46    Tmpfs,
47    Ufs,
48    Zfs,
49}
50
51/// An extension trait for `File` that allows querying the kind of filesystem it resides on.
52// todo: is there a possible error condition that isn't covered by `std::io::Error`?
53pub trait FileExt {
54    /// Query the filesystem that this file resides on.
55    /// 
56    /// # Errors
57    /// 
58    /// Note that a variant of `std::io::Error` may be returned,
59    /// which can indicate a number of error conditions
60    /// such as corrupted data, insufficient permissions, etc.
61    fn filesystem(&self) -> Result<FileSystemKind, std::io::Error>;
62}
63
64cfg_if::cfg_if! {
65    if #[cfg(target_os = "freebsd")] {
66        mod freebsd;
67        use freebsd::filesystem;
68    } else if #[cfg(target_os = "windows")] {
69        mod windows;
70        use windows::filesystem;
71    } else if #[cfg(target_os = "macos")] {
72        mod macos;
73        use macos::filesystem;
74    } else if #[cfg(target_os = "linux")] {
75        mod linux;
76        use linux::filesystem;
77    } else {
78        // todo: ios, android, and other bsd's
79        // todo: unix-y/posix-y fallback?
80    }
81}
82
83impl FileExt for File {
84    fn filesystem(&self) -> Result<FileSystemKind, std::io::Error> {
85        filesystem(self)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn look_for_unrecognized_filesystems() {
95        use std::collections::HashSet;
96        use walkdir::WalkDir;
97
98        let mut set = HashSet::new();
99
100        for entry in WalkDir::new("/").into_iter().filter_map(Result::ok) {
101            if let Ok(file) = File::open(entry.path()) {
102                if let Ok(FileSystemKind::Unrecognized(s)) = file.filesystem() {
103                    // panic!("unknown filesystem: {:?}", s);
104                    set.insert(s);
105                }
106            }
107        }
108
109        if !set.is_empty() {
110            panic!("{:?}", set);
111        }
112    }
113}