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}