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