1 | use std::future::Future; |
2 | |
3 | use crate::fs::File; |
4 | use crate::io; |
5 | use crate::path::Path; |
6 | use crate::task::spawn_blocking; |
7 | |
8 | /// A builder for opening files with configurable options. |
9 | /// |
10 | /// Files can be opened in [`read`] and/or [`write`] mode. |
11 | /// |
12 | /// The [`append`] option opens files in a special writing mode that moves the file cursor to the |
13 | /// end of file before every write operation. |
14 | /// |
15 | /// It is also possible to [`truncate`] the file right after opening, to [`create`] a file if it |
16 | /// doesn't exist yet, or to always create a new file with [`create_new`]. |
17 | /// |
18 | /// This type is an async version of [`std::fs::OpenOptions`]. |
19 | /// |
20 | /// [`read`]: #method.read |
21 | /// [`write`]: #method.write |
22 | /// [`append`]: #method.append |
23 | /// [`truncate`]: #method.truncate |
24 | /// [`create`]: #method.create |
25 | /// [`create_new`]: #method.create_new |
26 | /// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html |
27 | /// |
28 | /// # Examples |
29 | /// |
30 | /// Open a file for reading: |
31 | /// |
32 | /// ```no_run |
33 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
34 | /// # |
35 | /// use async_std::fs::OpenOptions; |
36 | /// |
37 | /// let file = OpenOptions::new() |
38 | /// .read(true) |
39 | /// .open("a.txt" ) |
40 | /// .await?; |
41 | /// # |
42 | /// # Ok(()) }) } |
43 | /// ``` |
44 | /// |
45 | /// Open a file for both reading and writing, and create it if it doesn't exist yet: |
46 | /// |
47 | /// ```no_run |
48 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
49 | /// # |
50 | /// use async_std::fs::OpenOptions; |
51 | /// |
52 | /// let file = OpenOptions::new() |
53 | /// .read(true) |
54 | /// .write(true) |
55 | /// .create(true) |
56 | /// .open("a.txt" ) |
57 | /// .await?; |
58 | /// # |
59 | /// # Ok(()) }) } |
60 | /// ``` |
61 | #[derive (Clone, Debug)] |
62 | pub struct OpenOptions(std::fs::OpenOptions); |
63 | |
64 | impl OpenOptions { |
65 | /// Creates a blank set of options. |
66 | /// |
67 | /// All options are initially set to `false`. |
68 | /// |
69 | /// # Examples |
70 | /// |
71 | /// ```no_run |
72 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
73 | /// # |
74 | /// use async_std::fs::OpenOptions; |
75 | /// |
76 | /// let file = OpenOptions::new() |
77 | /// .read(true) |
78 | /// .open("a.txt" ) |
79 | /// .await?; |
80 | /// # |
81 | /// # Ok(()) }) } |
82 | /// ``` |
83 | pub fn new() -> OpenOptions { |
84 | OpenOptions(std::fs::OpenOptions::new()) |
85 | } |
86 | |
87 | /// Configures the option for read mode. |
88 | /// |
89 | /// When set to `true`, this option means the file will be readable after opening. |
90 | /// |
91 | /// # Examples |
92 | /// |
93 | /// ```no_run |
94 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
95 | /// # |
96 | /// use async_std::fs::OpenOptions; |
97 | /// |
98 | /// let file = OpenOptions::new() |
99 | /// .read(true) |
100 | /// .open("a.txt" ) |
101 | /// .await?; |
102 | /// # |
103 | /// # Ok(()) }) } |
104 | /// ``` |
105 | pub fn read(&mut self, read: bool) -> &mut OpenOptions { |
106 | self.0.read(read); |
107 | self |
108 | } |
109 | |
110 | /// Configures the option for write mode. |
111 | /// |
112 | /// When set to `true`, this option means the file will be writable after opening. |
113 | /// |
114 | /// If the file already exists, write calls on it will overwrite the previous contents without |
115 | /// truncating it. |
116 | /// |
117 | /// # Examples |
118 | /// |
119 | /// ```no_run |
120 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
121 | /// # |
122 | /// use async_std::fs::OpenOptions; |
123 | /// |
124 | /// let file = OpenOptions::new() |
125 | /// .write(true) |
126 | /// .open("a.txt" ) |
127 | /// .await?; |
128 | /// # |
129 | /// # Ok(()) }) } |
130 | /// ``` |
131 | pub fn write(&mut self, write: bool) -> &mut OpenOptions { |
132 | self.0.write(write); |
133 | self |
134 | } |
135 | |
136 | /// Configures the option for append mode. |
137 | /// |
138 | /// When set to `true`, this option means the file will be writable after opening and the file |
139 | /// cursor will be moved to the end of file before every write operation. |
140 | /// |
141 | /// # Examples |
142 | /// |
143 | /// ```no_run |
144 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
145 | /// # |
146 | /// use async_std::fs::OpenOptions; |
147 | /// |
148 | /// let file = OpenOptions::new() |
149 | /// .append(true) |
150 | /// .open("a.txt" ) |
151 | /// .await?; |
152 | /// # |
153 | /// # Ok(()) }) } |
154 | /// ``` |
155 | pub fn append(&mut self, append: bool) -> &mut OpenOptions { |
156 | self.0.append(append); |
157 | self |
158 | } |
159 | |
160 | /// Configures the option for truncating the previous file. |
161 | /// |
162 | /// When set to `true`, the file will be truncated to the length of 0 bytes. |
163 | /// |
164 | /// The file must be opened in [`write`] or [`append`] mode for truncation to work. |
165 | /// |
166 | /// [`write`]: #method.write |
167 | /// [`append`]: #method.append |
168 | /// |
169 | /// # Examples |
170 | /// |
171 | /// ```no_run |
172 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
173 | /// # |
174 | /// use async_std::fs::OpenOptions; |
175 | /// |
176 | /// let file = OpenOptions::new() |
177 | /// .write(true) |
178 | /// .truncate(true) |
179 | /// .open("a.txt" ) |
180 | /// .await?; |
181 | /// # |
182 | /// # Ok(()) }) } |
183 | /// ``` |
184 | pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { |
185 | self.0.truncate(truncate); |
186 | self |
187 | } |
188 | |
189 | /// Configures the option for creating a new file if it doesn't exist. |
190 | /// |
191 | /// When set to `true`, this option means a new file will be created if it doesn't exist. |
192 | /// |
193 | /// The file must be opened in [`write`] or [`append`] mode for file creation to work. |
194 | /// |
195 | /// [`write`]: #method.write |
196 | /// [`append`]: #method.append |
197 | /// |
198 | /// # Examples |
199 | /// |
200 | /// ```no_run |
201 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
202 | /// # |
203 | /// use async_std::fs::OpenOptions; |
204 | /// |
205 | /// let file = OpenOptions::new() |
206 | /// .write(true) |
207 | /// .create(true) |
208 | /// .open("a.txt" ) |
209 | /// .await?; |
210 | /// # |
211 | /// # Ok(()) }) } |
212 | /// ``` |
213 | pub fn create(&mut self, create: bool) -> &mut OpenOptions { |
214 | self.0.create(create); |
215 | self |
216 | } |
217 | |
218 | /// Configures the option for creating a new file or failing if it already exists. |
219 | /// |
220 | /// When set to `true`, this option means a new file will be created, or the open operation |
221 | /// will fail if the file already exists. |
222 | /// |
223 | /// The file must be opened in [`write`] or [`append`] mode for file creation to work. |
224 | /// |
225 | /// [`write`]: #method.write |
226 | /// [`append`]: #method.append |
227 | /// |
228 | /// # Examples |
229 | /// |
230 | /// ```no_run |
231 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
232 | /// # |
233 | /// use async_std::fs::OpenOptions; |
234 | /// |
235 | /// let file = OpenOptions::new() |
236 | /// .write(true) |
237 | /// .create_new(true) |
238 | /// .open("a.txt" ) |
239 | /// .await?; |
240 | /// # |
241 | /// # Ok(()) }) } |
242 | /// ``` |
243 | pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { |
244 | self.0.create_new(create_new); |
245 | self |
246 | } |
247 | |
248 | /// Opens a file with the configured options. |
249 | /// |
250 | /// # Errors |
251 | /// |
252 | /// An error will be returned in the following situations: |
253 | /// |
254 | /// * The file does not exist and neither [`create`] nor [`create_new`] were set. |
255 | /// * The file's parent directory does not exist. |
256 | /// * The current process lacks permissions to open the file in the configured mode. |
257 | /// * The file already exists and [`create_new`] was set. |
258 | /// * Invalid combination of options was used, like [`truncate`] was set but [`write`] wasn't, |
259 | /// or none of [`read`], [`write`], and [`append`] modes was set. |
260 | /// * An OS-level occurred, like too many files are open or the file name is too long. |
261 | /// * Some other I/O error occurred. |
262 | /// |
263 | /// [`read`]: #method.read |
264 | /// [`write`]: #method.write |
265 | /// [`append`]: #method.append |
266 | /// [`truncate`]: #method.truncate |
267 | /// [`create`]: #method.create |
268 | /// [`create_new`]: #method.create_new |
269 | /// |
270 | /// # Examples |
271 | /// |
272 | /// ```no_run |
273 | /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { |
274 | /// # |
275 | /// use async_std::fs::OpenOptions; |
276 | /// |
277 | /// let file = OpenOptions::new() |
278 | /// .read(true) |
279 | /// .open("a.txt" ) |
280 | /// .await?; |
281 | /// # |
282 | /// # Ok(()) }) } |
283 | /// ``` |
284 | pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> { |
285 | let path = path.as_ref().to_owned(); |
286 | let options = self.0.clone(); |
287 | async move { |
288 | let file = spawn_blocking(move || options.open(path)).await?; |
289 | Ok(File::new(file, true)) |
290 | } |
291 | } |
292 | } |
293 | |
294 | impl Default for OpenOptions { |
295 | fn default() -> Self { |
296 | Self::new() |
297 | } |
298 | } |
299 | |
300 | cfg_unix! { |
301 | use crate::os::unix::fs::OpenOptionsExt; |
302 | |
303 | impl OpenOptionsExt for OpenOptions { |
304 | fn mode(&mut self, mode: u32) -> &mut Self { |
305 | self.0.mode(mode); |
306 | self |
307 | } |
308 | |
309 | fn custom_flags(&mut self, flags: i32) -> &mut Self { |
310 | self.0.custom_flags(flags); |
311 | self |
312 | } |
313 | } |
314 | } |
315 | |