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