Skip to main content

path_extra/std/
fs.rs

1#[cfg(unix)]
2use std::os::unix::{self, fs::PermissionsExt as _};
3use std::{
4    fs::{File, OpenOptions, Permissions},
5    io,
6    path::Path,
7};
8
9pub trait FileExt {
10    fn create_new_if_not_exists(path: impl AsRef<Path>) -> io::Result<Option<Self>>
11    where
12        Self: Sized;
13
14    fn create_if_not_exists(path: impl AsRef<Path>) -> io::Result<Option<Self>>
15    where
16        Self: Sized;
17
18    fn open_if_exists(path: impl AsRef<Path>) -> io::Result<Option<Self>>
19    where
20        Self: Sized;
21
22    fn with_permissions(&self, perm: Permissions) -> io::Result<&Self>;
23
24    fn with_permissions_readonly(&self, readonly: bool) -> io::Result<&Self>;
25
26    #[cfg(unix)]
27    fn with_permissions_mode(&self, mode: u32) -> io::Result<&Self>;
28    #[cfg(unix)]
29    fn add_permissions_mode(&self, mode: u32) -> io::Result<&Self>;
30    #[cfg(unix)]
31    fn remove_permissions_mode(&self, mode: u32) -> io::Result<&Self>;
32
33    #[cfg(unix)]
34    fn chown(&self, uid: Option<u32>, gid: Option<u32>) -> io::Result<&Self>;
35}
36
37impl FileExt for File {
38    #[inline]
39    fn create_new_if_not_exists(path: impl AsRef<Path>) -> io::Result<Option<Self>> {
40        match Self::create_new(path) {
41            Ok(file) => Ok(Some(file)),
42            Err(err) if err.kind() == io::ErrorKind::AlreadyExists => Ok(None),
43            Err(err) => Err(err),
44        }
45    }
46
47    #[inline]
48    fn create_if_not_exists(path: impl AsRef<Path>) -> io::Result<Option<Self>> {
49        let mut options = Self::options();
50
51        options.write(true).create_new(true);
52
53        options.open_if_not_exists(path)
54    }
55
56    #[inline]
57    fn open_if_exists(path: impl AsRef<Path>) -> io::Result<Option<Self>> {
58        match Self::open(path) {
59            Ok(file) => Ok(Some(file)),
60            Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
61            Err(err) => Err(err),
62        }
63    }
64
65    #[inline]
66    fn with_permissions(&self, perm: Permissions) -> io::Result<&Self> {
67        self.set_permissions(perm)?;
68
69        Ok(self)
70    }
71
72    #[inline]
73    fn with_permissions_readonly(&self, readonly: bool) -> io::Result<&Self> {
74        let meta = self.metadata()?;
75
76        let mut perm = meta.permissions();
77
78        perm.set_readonly(readonly);
79
80        self.with_permissions(perm)
81    }
82
83    #[cfg(unix)]
84    #[inline]
85    fn with_permissions_mode(&self, mode: u32) -> io::Result<&Self> {
86        let perm = Permissions::from_mode(mode);
87
88        self.with_permissions(perm)
89    }
90
91    #[cfg(unix)]
92    #[inline]
93    fn add_permissions_mode(&self, mode: u32) -> io::Result<&Self> {
94        let meta = self.metadata()?;
95
96        let perm = meta.permissions();
97
98        self.with_permissions_mode(perm.mode() | mode)
99    }
100
101    #[cfg(unix)]
102    #[inline]
103    fn remove_permissions_mode(&self, mode: u32) -> io::Result<&Self> {
104        let meta = self.metadata()?;
105
106        let perm = meta.permissions();
107
108        self.with_permissions_mode(perm.mode() & !mode)
109    }
110
111    #[cfg(unix)]
112    #[inline]
113    fn chown(&self, uid: Option<u32>, gid: Option<u32>) -> io::Result<&Self> {
114        unix::fs::fchown(self, uid, gid)?;
115
116        Ok(self)
117    }
118}
119
120pub trait OpenOptionsExt {
121    fn open_if_not_exists(&self, path: impl AsRef<Path>) -> io::Result<Option<File>>;
122    fn open_if_exists(&self, path: impl AsRef<Path>) -> io::Result<Option<File>>;
123}
124
125impl OpenOptionsExt for OpenOptions {
126    #[inline]
127    fn open_if_not_exists(&self, path: impl AsRef<Path>) -> io::Result<Option<File>> {
128        match self.open(path) {
129            Ok(file) => Ok(Some(file)),
130            Err(err) if err.kind() == io::ErrorKind::AlreadyExists => Ok(None),
131            Err(err) => Err(err),
132        }
133    }
134
135    #[inline]
136    fn open_if_exists(&self, path: impl AsRef<Path>) -> io::Result<Option<File>> {
137        match self.open(path) {
138            Ok(file) => Ok(Some(file)),
139            Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
140            Err(err) => Err(err),
141        }
142    }
143}