openssh_sftp_client/
metadata.rs1use super::{
2 lowlevel::{FileAttrs, FileType as SftpFileType, Permissions as SftpPermissions},
3 UnixTimeStamp,
4};
5
6#[derive(Debug, Default, Copy, Clone)]
8pub struct MetaDataBuilder(FileAttrs);
9
10impl MetaDataBuilder {
11 pub const fn new() -> Self {
13 Self(FileAttrs::new())
14 }
15
16 pub fn reset(&mut self) -> &mut Self {
18 self.0 = FileAttrs::new();
19 self
20 }
21
22 pub fn id(&mut self, (uid, gid): (u32, u32)) -> &mut Self {
24 self.0.set_id(uid, gid);
25 self
26 }
27
28 pub fn permissions(&mut self, perm: Permissions) -> &mut Self {
30 self.0.set_permissions(perm.0);
31 self
32 }
33
34 pub fn len(&mut self, len: u64) -> &mut Self {
36 self.0.set_size(len);
37 self
38 }
39
40 pub fn time(&mut self, accessed: UnixTimeStamp, modified: UnixTimeStamp) -> &mut Self {
42 self.0.set_time(accessed.0, modified.0);
43 self
44 }
45
46 pub fn create(&self) -> MetaData {
48 MetaData::new(self.0)
49 }
50}
51
52#[repr(transparent)]
54#[derive(Debug, Clone, Copy, Eq, PartialEq)]
55pub struct MetaData(FileAttrs);
56
57#[allow(clippy::len_without_is_empty)]
58impl MetaData {
59 pub(super) fn new(attrs: FileAttrs) -> Self {
60 Self(attrs)
61 }
62
63 pub(super) fn into_inner(self) -> FileAttrs {
64 self.0
65 }
66
67 pub fn len(&self) -> Option<u64> {
72 self.0.get_size()
73 }
74
75 pub fn uid(&self) -> Option<u32> {
80 self.0.get_id().map(|(uid, _gid)| uid)
81 }
82
83 pub fn gid(&self) -> Option<u32> {
88 self.0.get_id().map(|(_uid, gid)| gid)
89 }
90
91 pub fn permissions(&self) -> Option<Permissions> {
96 self.0.get_permissions().map(Permissions)
97 }
98
99 pub fn file_type(&self) -> Option<FileType> {
104 self.0.get_filetype().map(FileType)
105 }
106
107 pub fn accessed(&self) -> Option<UnixTimeStamp> {
112 self.0
113 .get_time()
114 .map(|(atime, _mtime)| atime)
115 .map(UnixTimeStamp)
116 }
117
118 pub fn modified(&self) -> Option<UnixTimeStamp> {
123 self.0
124 .get_time()
125 .map(|(_atime, mtime)| mtime)
126 .map(UnixTimeStamp)
127 }
128}
129
130#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
133pub struct FileType(SftpFileType);
134
135impl FileType {
136 pub fn is_dir(&self) -> bool {
138 self.0 == SftpFileType::Directory
139 }
140
141 pub fn is_file(&self) -> bool {
143 self.0 == SftpFileType::RegularFile
144 }
145
146 pub fn is_symlink(&self) -> bool {
148 self.0 == SftpFileType::Symlink
149 }
150
151 pub fn is_fifo(&self) -> bool {
153 self.0 == SftpFileType::FIFO
154 }
155
156 pub fn is_socket(&self) -> bool {
158 self.0 == SftpFileType::Socket
159 }
160
161 pub fn is_block_device(&self) -> bool {
163 self.0 == SftpFileType::BlockDevice
164 }
165
166 pub fn is_char_device(&self) -> bool {
168 self.0 == SftpFileType::CharacterDevice
169 }
170}
171
172#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
174pub struct Permissions(SftpPermissions);
175
176macro_rules! impl_getter_setter {
177 ($getter_name:ident, $setter_name:ident, $variant:ident, $variant_name:expr) => {
178 #[doc = "Tests whether "]
179 #[doc = $variant_name]
180 #[doc = " bit is set."]
181 pub fn $getter_name(&self) -> bool {
182 self.0.intersects(SftpPermissions::$variant)
183 }
184
185 #[doc = "Modify the "]
186 #[doc = $variant_name]
187 #[doc = " bit."]
188 pub fn $setter_name(&mut self, value: bool) -> &mut Self {
189 self.0.set(SftpPermissions::$variant, value);
190 self
191 }
192 };
193}
194
195impl Permissions {
196 pub const fn new() -> Self {
199 Self(SftpPermissions::empty())
200 }
201
202 impl_getter_setter!(suid, set_suid, SET_UID, "set-user-id");
203 impl_getter_setter!(sgid, set_sgid, SET_GID, "set-group-id");
204 impl_getter_setter!(svtx, set_vtx, SET_VTX, "set-sticky-bit");
205
206 impl_getter_setter!(
207 read_by_owner,
208 set_read_by_owner,
209 READ_BY_OWNER,
210 "read by owner"
211 );
212 impl_getter_setter!(
213 write_by_owner,
214 set_write_by_owner,
215 WRITE_BY_OWNER,
216 "write by owner"
217 );
218 impl_getter_setter!(
219 execute_by_owner,
220 set_execute_by_owner,
221 EXECUTE_BY_OWNER,
222 "execute by owner"
223 );
224
225 impl_getter_setter!(
226 read_by_group,
227 set_read_by_group,
228 READ_BY_GROUP,
229 "read by group"
230 );
231 impl_getter_setter!(
232 write_by_group,
233 set_write_by_group,
234 WRITE_BY_GROUP,
235 "write by group"
236 );
237 impl_getter_setter!(
238 execute_by_group,
239 set_execute_by_group,
240 EXECUTE_BY_GROUP,
241 "execute by group"
242 );
243
244 impl_getter_setter!(
245 read_by_other,
246 set_read_by_other,
247 READ_BY_OTHER,
248 "read by other"
249 );
250 impl_getter_setter!(
251 write_by_other,
252 set_write_by_other,
253 WRITE_BY_OTHER,
254 "write by other"
255 );
256 impl_getter_setter!(
257 execute_by_other,
258 set_execute_by_other,
259 EXECUTE_BY_OTHER,
260 "execute by other"
261 );
262
263 pub fn readonly(&self) -> bool {
266 !self.write_by_owner() && !self.write_by_group() && !self.write_by_other()
267 }
268
269 pub fn set_readonly(&mut self, readonly: bool) {
281 let writable = !readonly;
282
283 self.set_write_by_owner(writable);
284 self.set_write_by_group(writable);
285 self.set_write_by_other(writable);
286 }
287}
288
289impl From<u16> for Permissions {
290 fn from(octet: u16) -> Self {
315 let mut result = Permissions::new();
316
317 result.set_execute_by_other(octet & 0o1 != 0);
319 result.set_write_by_other(octet & 0o2 != 0);
320 result.set_read_by_other(octet & 0o4 != 0);
321
322 result.set_execute_by_group(octet & 0o10 != 0);
324 result.set_write_by_group(octet & 0o20 != 0);
325 result.set_read_by_group(octet & 0o40 != 0);
326
327 result.set_execute_by_owner(octet & 0o100 != 0);
329 result.set_write_by_owner(octet & 0o200 != 0);
330 result.set_read_by_owner(octet & 0o400 != 0);
331
332 result.set_vtx(octet & 0o1000 != 0);
334 result.set_sgid(octet & 0o2000 != 0);
335 result.set_sgid(octet & 0o4000 != 0);
336
337 result
338 }
339}
340
341impl Default for Permissions {
342 fn default() -> Self {
343 Self::new()
344 }
345}