cap_primitives/fs/
file_type.rs

1//! The `FileType` struct.
2
3use crate::fs::ImplFileTypeExt;
4
5/// `FileType`'s inner state.
6#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
7enum Inner {
8    /// A directory.
9    Dir,
10
11    /// A file.
12    File,
13
14    /// An unknown entity.
15    Unknown,
16
17    /// A `FileTypeExt` type.
18    Ext(ImplFileTypeExt),
19}
20
21/// A structure representing a type of file with accessors for each file type.
22///
23/// This corresponds to [`std::fs::FileType`].
24///
25/// <details>
26/// We need to define our own version because the libstd `FileType` doesn't
27/// have a public constructor that we can use.
28/// </details>
29#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
30#[repr(transparent)]
31pub struct FileType(Inner);
32
33impl FileType {
34    /// Creates a `FileType` for which `is_dir()` returns `true`.
35    #[inline]
36    pub const fn dir() -> Self {
37        Self(Inner::Dir)
38    }
39
40    /// Creates a `FileType` for which `is_file()` returns `true`.
41    #[inline]
42    pub const fn file() -> Self {
43        Self(Inner::File)
44    }
45
46    /// Creates a `FileType` for which `is_unknown()` returns `true`.
47    #[inline]
48    pub const fn unknown() -> Self {
49        Self(Inner::Unknown)
50    }
51
52    /// Creates a `FileType` from extension type.
53    #[inline]
54    pub(crate) const fn ext(ext: ImplFileTypeExt) -> Self {
55        Self(Inner::Ext(ext))
56    }
57
58    /// Tests whether this file type represents a directory.
59    ///
60    /// This corresponds to [`std::fs::FileType::is_dir`].
61    #[inline]
62    pub fn is_dir(&self) -> bool {
63        self.0 == Inner::Dir
64    }
65
66    /// Tests whether this file type represents a regular file.
67    ///
68    /// This corresponds to [`std::fs::FileType::is_file`].
69    #[inline]
70    pub fn is_file(&self) -> bool {
71        self.0 == Inner::File
72    }
73
74    /// Tests whether this file type represents a symbolic link.
75    ///
76    /// This corresponds to [`std::fs::FileType::is_symlink`].
77    #[inline]
78    pub fn is_symlink(&self) -> bool {
79        if let Inner::Ext(ext) = self.0 {
80            ext.is_symlink()
81        } else {
82            false
83        }
84    }
85}
86
87/// Unix-specific extensions for [`FileType`].
88///
89/// This corresponds to [`std::os::unix::fs::FileTypeExt`].
90#[cfg(any(unix, target_os = "vxworks"))]
91pub trait FileTypeExt {
92    /// Returns `true` if this file type is a block device.
93    fn is_block_device(&self) -> bool;
94    /// Returns `true` if this file type is a character device.
95    fn is_char_device(&self) -> bool;
96    /// Returns `true` if this file type is a fifo.
97    fn is_fifo(&self) -> bool;
98    /// Returns `true` if this file type is a socket.
99    fn is_socket(&self) -> bool;
100}
101
102#[cfg(any(unix, target_os = "vxworks"))]
103impl FileTypeExt for FileType {
104    #[inline]
105    fn is_block_device(&self) -> bool {
106        self.0 == Inner::Ext(ImplFileTypeExt::block_device())
107    }
108
109    #[inline]
110    fn is_char_device(&self) -> bool {
111        self.0 == Inner::Ext(ImplFileTypeExt::char_device())
112    }
113
114    #[inline]
115    fn is_fifo(&self) -> bool {
116        self.0 == Inner::Ext(ImplFileTypeExt::fifo())
117    }
118
119    #[inline]
120    fn is_socket(&self) -> bool {
121        self.0 == Inner::Ext(ImplFileTypeExt::socket())
122    }
123}
124
125/// Windows-specific extensions for [`FileType`].
126///
127/// This corresponds to [`std::os::windows::fs::FileTypeExt`].
128#[cfg(all(windows, windows_file_type_ext))]
129pub trait FileTypeExt {
130    /// Returns `true` if this file type is a symbolic link that is also a
131    /// directory.
132    fn is_symlink_dir(&self) -> bool;
133    /// Returns `true` if this file type is a symbolic link that is also a
134    /// file.
135    fn is_symlink_file(&self) -> bool;
136}
137
138#[cfg(all(windows, windows_file_type_ext))]
139impl FileTypeExt for FileType {
140    #[inline]
141    fn is_symlink_dir(&self) -> bool {
142        self.0 == Inner::Ext(ImplFileTypeExt::symlink_dir())
143    }
144
145    #[inline]
146    fn is_symlink_file(&self) -> bool {
147        self.0 == Inner::Ext(ImplFileTypeExt::symlink_file())
148    }
149}
150
151/// Extension trait to allow `is_block_device` etc. to be exposed by
152/// the `cap-fs-ext` crate.
153///
154/// This is hidden from the main API since this functionality isn't present in
155/// `std`. Use `cap_fs_ext::FileTypeExt` instead of calling this directly.
156#[cfg(windows)]
157#[doc(hidden)]
158pub trait _WindowsFileTypeExt {
159    fn is_block_device(&self) -> bool;
160    fn is_char_device(&self) -> bool;
161    fn is_fifo(&self) -> bool;
162    fn is_socket(&self) -> bool;
163}