1use std::fmt;
4use std::io;
5use std::time::Duration;
6use std::time::SystemTime;
7use std::time::UNIX_EPOCH;
8
9pub struct Metadata(pub(crate) libc::statx);
11
12impl Metadata {
13 pub fn file_type(&self) -> FileType {
19 FileType(self.0.stx_mode)
20 }
21
22 pub fn is_dir(&self) -> bool {
24 self.file_type().is_dir()
25 }
26
27 pub fn is_file(&self) -> bool {
29 self.file_type().is_file()
30 }
31
32 pub fn is_symlink(&self) -> bool {
34 self.file_type().is_symlink()
35 }
36
37 pub fn len(&self) -> u64 {
39 self.0.stx_size
40 }
41
42 pub fn is_empty(&self) -> bool {
44 self.0.stx_size == 0
45 }
46
47 pub fn permissions(&self) -> Permissions {
49 Permissions(self.0.stx_mode as u32 & 0o7777)
50 }
51
52 pub fn modified(&self) -> io::Result<SystemTime> {
54 Ok(system_time_from_unix(
55 self.0.stx_mtime.tv_sec,
56 self.0.stx_mtime.tv_nsec,
57 ))
58 }
59
60 pub fn accessed(&self) -> io::Result<SystemTime> {
62 Ok(system_time_from_unix(
63 self.0.stx_atime.tv_sec,
64 self.0.stx_atime.tv_nsec,
65 ))
66 }
67
68 pub fn created(&self) -> io::Result<SystemTime> {
70 Ok(system_time_from_unix(
71 self.0.stx_btime.tv_sec,
72 self.0.stx_btime.tv_nsec,
73 ))
74 }
75
76 pub fn dev(&self) -> u64 {
81 libc::makedev(self.0.stx_dev_major, self.0.stx_dev_minor) as u64
82 }
83
84 pub fn ino(&self) -> u64 {
85 self.0.stx_ino
86 }
87
88 pub fn mode(&self) -> u32 {
89 self.0.stx_mode as u32
90 }
91
92 pub fn nlink(&self) -> u64 {
93 self.0.stx_nlink as u64
94 }
95
96 pub fn uid(&self) -> u32 {
97 self.0.stx_uid
98 }
99
100 pub fn gid(&self) -> u32 {
101 self.0.stx_gid
102 }
103
104 pub fn rdev(&self) -> u64 {
105 libc::makedev(self.0.stx_rdev_major, self.0.stx_rdev_minor) as u64
106 }
107
108 pub fn size(&self) -> u64 {
109 self.0.stx_size
110 }
111
112 pub fn atime(&self) -> i64 {
113 self.0.stx_atime.tv_sec
114 }
115
116 pub fn atime_nsec(&self) -> i64 {
117 self.0.stx_atime.tv_nsec as i64
118 }
119
120 pub fn mtime(&self) -> i64 {
121 self.0.stx_mtime.tv_sec
122 }
123
124 pub fn mtime_nsec(&self) -> i64 {
125 self.0.stx_mtime.tv_nsec as i64
126 }
127
128 pub fn ctime(&self) -> i64 {
129 self.0.stx_ctime.tv_sec
130 }
131
132 pub fn ctime_nsec(&self) -> i64 {
133 self.0.stx_ctime.tv_nsec as i64
134 }
135
136 pub fn blksize(&self) -> u64 {
137 self.0.stx_blksize as u64
138 }
139
140 pub fn blocks(&self) -> u64 {
141 self.0.stx_blocks
142 }
143}
144
145impl fmt::Debug for Metadata {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.debug_struct("Metadata")
148 .field("file_type", &self.file_type())
149 .field("permissions", &self.permissions())
150 .field("len", &self.len())
151 .field("uid", &self.uid())
152 .field("gid", &self.gid())
153 .field("ino", &self.ino())
154 .finish_non_exhaustive()
155 }
156}
157
158#[derive(Clone, Copy, PartialEq, Eq, Hash)]
164pub struct FileType(u16);
165
166impl FileType {
167 pub fn is_dir(&self) -> bool {
168 (self.0 & libc::S_IFMT as u16) == libc::S_IFDIR as u16
169 }
170
171 pub fn is_file(&self) -> bool {
172 (self.0 & libc::S_IFMT as u16) == libc::S_IFREG as u16
173 }
174
175 pub fn is_symlink(&self) -> bool {
176 (self.0 & libc::S_IFMT as u16) == libc::S_IFLNK as u16
177 }
178
179 pub fn is_block_device(&self) -> bool {
180 (self.0 & libc::S_IFMT as u16) == libc::S_IFBLK as u16
181 }
182
183 pub fn is_char_device(&self) -> bool {
184 (self.0 & libc::S_IFMT as u16) == libc::S_IFCHR as u16
185 }
186
187 pub fn is_fifo(&self) -> bool {
188 (self.0 & libc::S_IFMT as u16) == libc::S_IFIFO as u16
189 }
190
191 pub fn is_socket(&self) -> bool {
192 (self.0 & libc::S_IFMT as u16) == libc::S_IFSOCK as u16
193 }
194}
195
196impl fmt::Debug for FileType {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 let kind = if self.is_file() {
199 "file"
200 } else if self.is_dir() {
201 "directory"
202 } else if self.is_symlink() {
203 "symlink"
204 } else if self.is_block_device() {
205 "block_device"
206 } else if self.is_char_device() {
207 "char_device"
208 } else if self.is_fifo() {
209 "fifo"
210 } else if self.is_socket() {
211 "socket"
212 } else {
213 "unknown"
214 };
215 write!(f, "FileType({kind})")
216 }
217}
218
219#[derive(Clone, Copy, PartialEq, Eq)]
225pub struct Permissions(u32);
226
227impl Permissions {
228 pub fn readonly(&self) -> bool {
229 (self.0 & 0o200) == 0
230 }
231
232 pub fn mode(&self) -> u32 {
233 self.0
234 }
235
236 pub fn from_mode(mode: u32) -> Self {
237 Self(mode & 0o7777)
238 }
239
240 pub fn set_readonly(&mut self, readonly: bool) {
241 if readonly {
242 self.0 &= !0o222;
243 } else {
244 self.0 |= 0o200;
245 }
246 }
247}
248
249impl fmt::Debug for Permissions {
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 write!(f, "Permissions({:04o})", self.0)
252 }
253}
254
255fn system_time_from_unix(sec: i64, nsec: u32) -> SystemTime {
260 if sec >= 0 {
261 UNIX_EPOCH + Duration::new(sec as u64, nsec)
262 } else {
263 UNIX_EPOCH - Duration::new((-sec) as u64, nsec)
264 }
265}