Skip to main content

path_extra/tokio/
fs.rs

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}