Skip to main content

compio_fs/utils/
mod.rs

1#[cfg(unix)]
2#[path = "unix.rs"]
3mod sys;
4
5#[cfg(windows)]
6#[path = "windows.rs"]
7mod sys;
8
9use std::{io, path::Path};
10
11use compio_buf::{BufResult, IoBuf, buf_try};
12use compio_io::{AsyncReadAtExt, AsyncWriteAtExt};
13
14use crate::{File, metadata};
15
16/// Removes a file from the filesystem.
17pub async fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
18    sys::remove_file(path).await
19}
20
21/// Removes an empty directory.
22pub async fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
23    sys::remove_dir(path).await
24}
25
26/// Creates a new, empty directory at the provided path.
27pub async fn create_dir(path: impl AsRef<Path>) -> io::Result<()> {
28    DirBuilder::new().create(path).await
29}
30
31/// Recursively create a directory and all of its parent components if they are
32/// missing.
33pub async fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
34    DirBuilder::new().recursive(true).create(path).await
35}
36
37/// Rename a file or directory to a new name, replacing the original file if
38/// `to` already exists.
39pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
40    sys::rename(from, to).await
41}
42
43/// Creates a new symbolic link on the filesystem.
44#[cfg(unix)]
45pub async fn symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
46    sys::symlink(original, link).await
47}
48
49/// Creates a new symlink to a non-directory file on the filesystem.
50#[cfg(windows)]
51pub async fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
52    sys::symlink_file(original, link).await
53}
54
55/// Creates a new symlink to a directory on the filesystem.
56#[cfg(windows)]
57pub async fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
58    sys::symlink_dir(original, link).await
59}
60
61/// Creates a new hard link on the filesystem.
62pub async fn hard_link(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
63    sys::hard_link(original, link).await
64}
65
66/// Write a slice as the entire contents of a file.
67///
68/// This function will create a file if it does not exist,
69/// and will entirely replace its contents if it does.
70pub async fn write<P: AsRef<Path>, B: IoBuf>(path: P, buf: B) -> BufResult<(), B> {
71    let (mut file, buf) = buf_try!(File::create(path).await, buf);
72    file.write_all_at(buf, 0).await
73}
74
75/// Read the entire contents of a file into a bytes vector.
76pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
77    let file = File::open(path).await?;
78    let BufResult(res, buffer) = file.read_to_end_at(Vec::new(), 0).await;
79    res?;
80    Ok(buffer)
81}
82
83/// A builder used to create directories in various manners.
84pub struct DirBuilder {
85    inner: sys::DirBuilder,
86    recursive: bool,
87}
88
89impl Default for DirBuilder {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95impl DirBuilder {
96    /// Creates a new set of options with default mode/security settings for all
97    /// platforms and also non-recursive.
98    pub fn new() -> Self {
99        Self {
100            inner: sys::DirBuilder::new(),
101            recursive: false,
102        }
103    }
104
105    /// Indicates that directories should be created recursively, creating all
106    /// parent directories. Parents that do not exist are created with the same
107    /// security and permissions settings.
108    pub fn recursive(&mut self, recursive: bool) -> &mut Self {
109        self.recursive = recursive;
110        self
111    }
112
113    /// Creates the specified directory with the options configured in this
114    /// builder.
115    pub async fn create(&self, path: impl AsRef<Path>) -> io::Result<()> {
116        let path = path.as_ref();
117        if self.recursive {
118            self.create_dir_all(path).await
119        } else {
120            self.inner.create(path).await
121        }
122    }
123
124    async fn create_dir_all(&self, path: &Path) -> io::Result<()> {
125        if path == Path::new("") {
126            return Ok(());
127        }
128
129        match self.inner.create(path).await {
130            Ok(()) => return Ok(()),
131            Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
132            Err(_) if metadata(path).await.map(|m| m.is_dir()).unwrap_or_default() => return Ok(()),
133            Err(e) => return Err(e),
134        }
135        match path.parent() {
136            Some(p) => Box::pin(self.create_dir_all(p)).await?,
137            None => {
138                return Err(io::Error::other("failed to create whole tree"));
139            }
140        }
141        match self.inner.create(path).await {
142            Ok(()) => Ok(()),
143            Err(_) if metadata(path).await.map(|m| m.is_dir()).unwrap_or_default() => Ok(()),
144            Err(e) => Err(e),
145        }
146    }
147
148    #[cfg(dirfd)]
149    pub(crate) async fn create_at(&self, dir: &File, path: &Path) -> io::Result<()> {
150        if path.is_absolute() {
151            self.create(path).await
152        } else if self.recursive {
153            self.create_dir_all_at(dir, path).await
154        } else {
155            self.inner.create_at(dir, path).await
156        }
157    }
158
159    #[cfg(dirfd)]
160    async fn create_dir_all_at(&self, dir: &File, path: &Path) -> io::Result<()> {
161        use crate::metadata_at;
162
163        if path == Path::new("") {
164            return Ok(());
165        }
166        match self.inner.create_at(dir, path).await {
167            Ok(()) => return Ok(()),
168            Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
169            Err(_)
170                if metadata_at(dir, path)
171                    .await
172                    .map(|m| m.is_dir())
173                    .unwrap_or_default() =>
174            {
175                return Ok(());
176            }
177            Err(e) => return Err(e),
178        }
179        match path.parent() {
180            Some(p) => Box::pin(self.create_dir_all_at(dir, p)).await?,
181            None => {
182                return Err(io::Error::other("failed to create whole tree"));
183            }
184        }
185        match self.inner.create_at(dir, path).await {
186            Ok(()) => Ok(()),
187            Err(_)
188                if metadata_at(dir, path)
189                    .await
190                    .map(|m| m.is_dir())
191                    .unwrap_or_default() =>
192            {
193                Ok(())
194            }
195            Err(e) => Err(e),
196        }
197    }
198}
199
200#[cfg(unix)]
201impl std::os::unix::fs::DirBuilderExt for DirBuilder {
202    fn mode(&mut self, mode: u32) -> &mut Self {
203        self.inner.mode(mode);
204        self
205    }
206}