eza/fs/
fields.rs

1// SPDX-FileCopyrightText: 2024 Christina Sørensen
2// SPDX-License-Identifier: EUPL-1.2
3//
4// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors
5// SPDX-FileCopyrightText: 2014 Benjamin Sago
6// SPDX-License-Identifier: MIT
7//! Wrapper types for the values returned from `File`s.
8//!
9//! The methods of `File` that return information about the entry on the
10//! filesystem -- size, modification date, block count, or Git status -- used
11//! to just return these as formatted strings, but this became inflexible once
12//! customisable output styles landed.
13//!
14//! Instead, they will return a wrapper type from this module, which tags the
15//! type with what field it is while containing the actual raw value.
16//!
17//! The `output::details` module, among others, uses these types to render and
18//! display the information as formatted strings.
19
20#![allow(non_camel_case_types)]
21#![allow(clippy::struct_excessive_bools)]
22
23/// The type of a file’s group ID.
24pub type gid_t = u32;
25
26/// The type of a file’s inode.
27#[allow(unused)]
28pub type ino_t = u64;
29
30/// The type of a file’s number of links.
31#[allow(unused)]
32pub type nlink_t = u64;
33
34/// The type of a file’s timestamp (creation, modification, access, etc).
35pub type time_t = i64;
36
37/// The type of a file’s user ID.
38#[allow(unused)]
39pub type uid_t = u32;
40
41/// The type of user file flags
42pub type flag_t = u32;
43
44/// The file’s base type, which gets displayed in the very first column of the
45/// details output.
46///
47/// This type is set entirely by the filesystem, rather than relying on a
48/// file’s contents. So “link” is a type, but “image” is just a type of
49/// regular file. (See the `filetype` module for those checks.)
50///
51/// Its ordering is used when sorting by type.
52#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
53pub enum Type {
54    Directory,
55    File,
56    Link,
57    Pipe,
58    Socket,
59    CharDevice,
60    BlockDevice,
61    Special,
62}
63
64impl Type {
65    #[must_use]
66    pub fn is_regular_file(self) -> bool {
67        matches!(self, Self::File)
68    }
69}
70
71/// The file’s Unix permission bitfield, with one entry per bit.
72#[derive(Copy, Clone)]
73#[rustfmt::skip]
74pub struct Permissions {
75    pub user_read:      bool,
76    pub user_write:     bool,
77    pub user_execute:   bool,
78
79    pub group_read:     bool,
80    pub group_write:    bool,
81    pub group_execute:  bool,
82
83    pub other_read:     bool,
84    pub other_write:    bool,
85    pub other_execute:  bool,
86
87    pub sticky:         bool,
88    pub setgid:         bool,
89    pub setuid:         bool,
90}
91
92/// The file's `FileAttributes` field, available only on Windows.
93#[derive(Copy, Clone)]
94#[rustfmt::skip]
95#[cfg(windows)]
96pub struct Attributes {
97    pub archive:         bool,
98    pub directory:       bool,
99    pub readonly:        bool,
100    pub hidden:          bool,
101    pub system:          bool,
102    pub reparse_point:   bool,
103}
104
105/// The three pieces of information that are displayed as a single column in
106/// the details view. These values are fused together to make the output a
107/// little more compressed.
108#[derive(Copy, Clone)]
109pub struct PermissionsPlus {
110    #[allow(unused)]
111    pub file_type: Type,
112    #[cfg(unix)]
113    pub permissions: Permissions,
114    #[cfg(windows)]
115    pub attributes: Attributes,
116    #[allow(unused)]
117    pub xattrs: bool,
118}
119
120/// The permissions encoded as octal values
121#[derive(Copy, Clone)]
122pub struct OctalPermissions {
123    pub permissions: Permissions,
124}
125
126/// A file’s number of hard links on the filesystem.
127///
128/// Under Unix, a file can exist on the filesystem only once but appear in
129/// multiple directories. However, it’s rare (but occasionally useful!) for a
130/// regular file to have a link count greater than 1, so we highlight the
131/// block count specifically for this case.
132#[allow(unused)]
133#[derive(Copy, Clone)]
134pub struct Links {
135    /// The actual link count.
136    pub count: nlink_t,
137
138    /// Whether this file is a regular file with more than one hard link.
139    pub multiple: bool,
140}
141
142/// A file’s inode. Every directory entry on a Unix filesystem has an inode,
143/// including directories and links, so this is applicable to everything exa
144/// can deal with.
145#[allow(unused)]
146#[derive(Copy, Clone)]
147pub struct Inode(pub ino_t);
148
149/// A file's size of allocated file system blocks.
150#[derive(Copy, Clone)]
151#[cfg(unix)]
152pub enum Blocksize {
153    /// This file has the given number of blocks.
154    Some(u64),
155
156    /// This file isn’t of a type that can take up blocks.
157    None,
158}
159
160/// The ID of the user that owns a file. This will only ever be a number;
161/// looking up the username is done in the `display` module.
162#[allow(unused)]
163#[derive(Copy, Clone)]
164pub struct User(pub uid_t);
165
166/// The ID of the group that a file belongs to.
167#[allow(unused)]
168#[derive(Copy, Clone)]
169pub struct Group(pub gid_t);
170
171/// A file’s size, in bytes. This is usually formatted by the `number_prefix`
172/// crate into something human-readable.
173#[derive(Copy, Clone)]
174pub enum Size {
175    /// This file has a defined size.
176    Some(u64),
177
178    /// This file has no size, or has a size but we aren’t interested in it.
179    ///
180    /// Under Unix, directory entries that aren’t regular files will still
181    /// have a file size. For example, a directory will just contain a list of
182    /// its files as its “contents” and will be specially flagged as being a
183    /// directory, rather than a file. However, seeing the “file size” of this
184    /// data is rarely useful — I can’t think of a time when I’ve seen it and
185    /// learnt something. So we discard it and just output “-” instead.
186    ///
187    /// See this answer for more: <https://unix.stackexchange.com/a/68266>
188    None,
189
190    /// This file is a block or character device, so instead of a size, print
191    /// out the file’s major and minor device IDs.
192    ///
193    /// This is what ls does as well. Without it, the devices will just have
194    /// file sizes of zero.
195    DeviceIDs(DeviceIDs),
196}
197
198/// The major and minor device IDs that gets displayed for device files.
199///
200/// You can see what these device numbers mean:
201/// - <http://www.lanana.org/docs/device-list/>
202/// - <http://www.lanana.org/docs/device-list/devices-2.6+.txt>
203#[derive(Copy, Clone)]
204pub struct DeviceIDs {
205    pub major: u32,
206    pub minor: u32,
207}
208
209/// One of a file’s timestamps (created, accessed, or modified).
210#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
211pub struct Time {
212    pub seconds: time_t,
213    pub nanoseconds: time_t,
214}
215
216/// A file’s status in a Git repository. Whether a file is in a repository or
217/// not is handled by the Git module, rather than having a “null” variant in
218/// this enum.
219#[derive(PartialEq, Eq, Copy, Clone)]
220pub enum GitStatus {
221    /// This file hasn’t changed since the last commit.
222    NotModified,
223
224    /// This file didn’t exist for the last commit, and is not specified in
225    /// the ignored files list.
226    New,
227
228    /// A file that’s been modified since the last commit.
229    Modified,
230
231    /// A deleted file. This can’t ever be shown, but it’s here anyway!
232    Deleted,
233
234    /// A file that Git has tracked a rename for.
235    Renamed,
236
237    /// A file that’s had its type (such as the file permissions) changed.
238    TypeChange,
239
240    /// A file that’s ignored (that matches a line in .gitignore)
241    Ignored,
242
243    /// A file that’s updated but unmerged.
244    Conflicted,
245}
246
247/// A file’s complete Git status. It’s possible to make changes to a file, add
248/// it to the staging area, then make *more* changes, so we need to list each
249/// file’s status for both of these.
250#[derive(Copy, Clone)]
251pub struct Git {
252    pub staged: GitStatus,
253    pub unstaged: GitStatus,
254}
255
256impl Default for Git {
257    /// Create a Git status for a file with nothing done to it.
258    fn default() -> Self {
259        Self {
260            staged: GitStatus::NotModified,
261            unstaged: GitStatus::NotModified,
262        }
263    }
264}
265
266pub enum SecurityContextType<'a> {
267    SELinux(&'a str),
268    None,
269}
270
271pub struct SecurityContext<'a> {
272    pub context: SecurityContextType<'a>,
273}
274
275#[allow(dead_code)]
276#[derive(PartialEq, Copy, Clone)]
277pub enum SubdirGitRepoStatus {
278    NoRepo,
279    GitClean,
280    GitDirty,
281}
282
283#[derive(Clone)]
284pub struct SubdirGitRepo {
285    pub status: Option<SubdirGitRepoStatus>,
286    pub branch: Option<String>,
287}
288
289impl Default for SubdirGitRepo {
290    fn default() -> Self {
291        Self {
292            status: Some(SubdirGitRepoStatus::NoRepo),
293            branch: None,
294        }
295    }
296}
297
298/// The user file flags on the file. This will only ever be a number;
299/// looking up the flags is done in the `display` module.
300pub struct Flags(pub flag_t);