Skip to main content

fancy_tree/tree/entry/attributes/
mod.rs

1//! Provides utilities for file objects.
2pub use directory::DirectoryAttributes;
3pub use file::FileAttributes;
4use std::fs::{self, File, Metadata};
5use std::io;
6use std::path::Path;
7pub use symlink::SymlinkAttributes;
8
9mod directory;
10mod file;
11mod interop;
12mod symlink;
13
14/// Attributes for a tree entry.
15pub enum Attributes {
16    /// A directory.
17    Directory(DirectoryAttributes),
18    /// A file.
19    File(FileAttributes),
20    /// A symlink.
21    Symlink(SymlinkAttributes),
22}
23
24impl Attributes {
25    /// Creates new [`Attributes`].
26    pub fn new<P>(path: P) -> io::Result<Self>
27    where
28        P: AsRef<Path>,
29    {
30        let path = path.as_ref();
31        let metadata = fs::metadata(path)?;
32        let file_type = metadata.file_type();
33
34        if file_type.is_symlink() {
35            Ok(Self::new_symlink())
36        } else if file_type.is_dir() {
37            Ok(Self::new_directory(metadata))
38        } else if file_type.is_file() {
39            let file = File::open(path)?;
40            Self::new_file(path, file, metadata)
41        } else {
42            // NOTE Just to make all file type checks a bit more explicit
43            unreachable!("Must be a symlink, directory, or file")
44        }
45    }
46
47    /// Creates file attributes.
48    #[inline]
49    fn new_file<P>(path: P, file: File, metadata: Metadata) -> io::Result<Self>
50    where
51        P: AsRef<Path>,
52    {
53        FileAttributes::new(path, file, metadata).map(Self::File)
54    }
55
56    /// Creates directory attributes.
57    #[inline]
58    fn new_directory(metadata: Metadata) -> Self {
59        Self::Directory(DirectoryAttributes::new(metadata))
60    }
61
62    /// Creates symlink attributes.
63    #[inline]
64    fn new_symlink() -> Self {
65        Self::Symlink(SymlinkAttributes)
66    }
67
68    /// Gets a reference to the file attributes.
69    #[inline]
70    pub fn file(&self) -> Option<&FileAttributes> {
71        if let Self::File(attributes) = self {
72            Some(attributes)
73        } else {
74            None
75        }
76    }
77
78    /// Gets a reference to the directory attributes.
79    #[inline]
80    pub fn directory(&self) -> Option<&DirectoryAttributes> {
81        if let Self::Directory(attributes) = self {
82            Some(attributes)
83        } else {
84            None
85        }
86    }
87
88    /// Gets a reference to the symlink attributes.
89    #[inline]
90    pub fn symlink(&self) -> Option<&SymlinkAttributes> {
91        if let Self::Symlink(attributes) = self {
92            Some(attributes)
93        } else {
94            None
95        }
96    }
97
98    /// Checks if the file is an executable.
99    pub fn is_executable(&self) -> bool {
100        self.is_file_and(|attributes| attributes.is_executable())
101    }
102
103    /// Checks if the attributes mark the file as hidden.
104    pub fn is_hidden(&self) -> bool {
105        self.is_file_and(|attributes| attributes.is_hidden())
106            || self.is_directory_and(|attributes| attributes.is_hidden())
107    }
108
109    /// Checks if the attributes are for a file.
110    #[inline]
111    pub const fn is_file(&self) -> bool {
112        matches!(self, Self::File(_))
113    }
114
115    /// If the attributes are for a file, calls `f` on the [`FileAttributes`].
116    pub fn is_file_and<F>(&self, f: F) -> bool
117    where
118        F: FnOnce(&FileAttributes) -> bool,
119    {
120        if let Self::File(attributes) = self {
121            f(attributes)
122        } else {
123            false
124        }
125    }
126
127    /// Checks if the attributes are for a directory.
128    #[inline]
129    pub const fn is_directory(&self) -> bool {
130        matches!(self, Self::Directory(_))
131    }
132
133    /// If the attributes are for a directory, calls `f` on the [`DirectoryAttributes`].
134    pub fn is_directory_and<F>(&self, f: F) -> bool
135    where
136        F: FnOnce(&DirectoryAttributes) -> bool,
137    {
138        if let Self::Directory(attributes) = self {
139            f(attributes)
140        } else {
141            false
142        }
143    }
144
145    /// Checks if the attributes are for a symlink.
146    #[inline]
147    pub const fn is_symlink(&self) -> bool {
148        matches!(self, Self::Symlink(_))
149    }
150}