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