1#[cfg(unix)]
2use std::os::unix::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
34impl FileExt for File {
35 #[inline]
36 fn create_new_if_not_exists(path: impl AsRef<Path>) -> io::Result<Option<Self>> {
37 match Self::create_new(path) {
38 Ok(file) => Ok(Some(file)),
39 Err(err) if err.kind() == io::ErrorKind::AlreadyExists => Ok(None),
40 Err(err) => Err(err),
41 }
42 }
43
44 #[inline]
45 fn create_if_not_exists(path: impl AsRef<Path>) -> io::Result<Option<Self>> {
46 let mut options = Self::options();
47
48 options.write(true).create_new(true);
49
50 options.open_if_not_exists(path)
51 }
52
53 #[inline]
54 fn open_if_exists(path: impl AsRef<Path>) -> io::Result<Option<Self>> {
55 match Self::open(path) {
56 Ok(file) => Ok(Some(file)),
57 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
58 Err(err) => Err(err),
59 }
60 }
61
62 #[inline]
63 fn with_permissions(&self, perm: Permissions) -> io::Result<&Self> {
64 self.set_permissions(perm)?;
65
66 Ok(self)
67 }
68
69 #[inline]
70 fn with_permissions_readonly(&self, readonly: bool) -> io::Result<&Self> {
71 let meta = self.metadata()?;
72
73 let mut perm = meta.permissions();
74
75 perm.set_readonly(readonly);
76
77 self.with_permissions(perm)
78 }
79
80 #[cfg(unix)]
81 #[inline]
82 fn with_permissions_mode(&self, mode: u32) -> io::Result<&Self> {
83 let perm = Permissions::from_mode(mode);
84
85 self.with_permissions(perm)
86 }
87
88 #[cfg(unix)]
89 #[inline]
90 fn add_permissions_mode(&self, mode: u32) -> io::Result<&Self> {
91 let meta = self.metadata()?;
92
93 let perm = meta.permissions();
94
95 self.with_permissions_mode(perm.mode() | mode)
96 }
97
98 #[cfg(unix)]
99 #[inline]
100 fn remove_permissions_mode(&self, mode: u32) -> io::Result<&Self> {
101 let meta = self.metadata()?;
102
103 let perm = meta.permissions();
104
105 self.with_permissions_mode(perm.mode() & !mode)
106 }
107}
108
109pub trait OpenOptionsExt {
110 fn open_if_not_exists(&self, path: impl AsRef<Path>) -> io::Result<Option<File>>;
111 fn open_if_exists(&self, path: impl AsRef<Path>) -> io::Result<Option<File>>;
112}
113
114impl OpenOptionsExt for OpenOptions {
115 #[inline]
116 fn open_if_not_exists(&self, path: impl AsRef<Path>) -> io::Result<Option<File>> {
117 match self.open(path) {
118 Ok(file) => Ok(Some(file)),
119 Err(err) if err.kind() == io::ErrorKind::AlreadyExists => Ok(None),
120 Err(err) => Err(err),
121 }
122 }
123
124 #[inline]
125 fn open_if_exists(&self, path: impl AsRef<Path>) -> io::Result<Option<File>> {
126 match self.open(path) {
127 Ok(file) => Ok(Some(file)),
128 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
129 Err(err) => Err(err),
130 }
131 }
132}