broker_tokio/fs/open_options.rs
1use crate::fs::{asyncify, File};
2
3use std::io;
4use std::path::Path;
5
6/// Options and flags which can be used to configure how a file is opened.
7///
8/// This builder exposes the ability to configure how a [`File`] is opened and
9/// what operations are permitted on the open file. The [`File::open`] and
10/// [`File::create`] methods are aliases for commonly used options using this
11/// builder.
12///
13/// Generally speaking, when using `OpenOptions`, you'll first call [`new`],
14/// then chain calls to methods to set each option, then call [`open`], passing
15/// the path of the file you're trying to open. This will give you a
16/// [`io::Result`][result] with a [`File`] inside that you can further operate
17/// on.
18///
19/// This is a specialized version of [`std::fs::OpenOptions`] for usage from
20/// the Tokio runtime.
21///
22/// `From<std::fs::OpenOptions>` is implemented for more advanced configuration
23/// than the methods provided here.
24///
25/// [`new`]: OpenOptions::new
26/// [`open`]: OpenOptions::open
27/// [result]: std::io::Result
28/// [`File`]: File
29/// [`File::open`]: File::open
30/// [`File::create`]: File::create
31/// [`std::fs::OpenOptions`]: std::fs::OpenOptions
32///
33/// # Examples
34///
35/// Opening a file to read:
36///
37/// ```no_run
38/// use tokio::fs::OpenOptions;
39/// use std::io;
40///
41/// #[tokio::main]
42/// async fn main() -> io::Result<()> {
43/// let file = OpenOptions::new()
44/// .read(true)
45/// .open("foo.txt")
46/// .await?;
47///
48/// Ok(())
49/// }
50/// ```
51///
52/// Opening a file for both reading and writing, as well as creating it if it
53/// doesn't exist:
54///
55/// ```no_run
56/// use tokio::fs::OpenOptions;
57/// use std::io;
58///
59/// #[tokio::main]
60/// async fn main() -> io::Result<()> {
61/// let file = OpenOptions::new()
62/// .read(true)
63/// .write(true)
64/// .create(true)
65/// .open("foo.txt")
66/// .await?;
67///
68/// Ok(())
69/// }
70/// ```
71#[derive(Clone, Debug)]
72pub struct OpenOptions(std::fs::OpenOptions);
73
74impl OpenOptions {
75 /// Creates a blank new set of options ready for configuration.
76 ///
77 /// All options are initially set to `false`.
78 ///
79 /// This is an async version of [`std::fs::OpenOptions::new`][std]
80 ///
81 /// [std]: std::fs::OpenOptions::new
82 ///
83 /// # Examples
84 ///
85 /// ```no_run
86 /// use tokio::fs::OpenOptions;
87 ///
88 /// let mut options = OpenOptions::new();
89 /// let future = options.read(true).open("foo.txt");
90 /// ```
91 pub fn new() -> OpenOptions {
92 OpenOptions(std::fs::OpenOptions::new())
93 }
94
95 /// Sets the option for read access.
96 ///
97 /// This option, when true, will indicate that the file should be
98 /// `read`-able if opened.
99 ///
100 /// This is an async version of [`std::fs::OpenOptions::read`][std]
101 ///
102 /// [std]: std::fs::OpenOptions::read
103 ///
104 /// # Examples
105 ///
106 /// ```no_run
107 /// use tokio::fs::OpenOptions;
108 /// use std::io;
109 ///
110 /// #[tokio::main]
111 /// async fn main() -> io::Result<()> {
112 /// let file = OpenOptions::new()
113 /// .read(true)
114 /// .open("foo.txt")
115 /// .await?;
116 ///
117 /// Ok(())
118 /// }
119 /// ```
120 pub fn read(&mut self, read: bool) -> &mut OpenOptions {
121 self.0.read(read);
122 self
123 }
124
125 /// Sets the option for write access.
126 ///
127 /// This option, when true, will indicate that the file should be
128 /// `write`-able if opened.
129 ///
130 /// This is an async version of [`std::fs::OpenOptions::write`][std]
131 ///
132 /// [std]: std::fs::OpenOptions::write
133 ///
134 /// # Examples
135 ///
136 /// ```no_run
137 /// use tokio::fs::OpenOptions;
138 /// use std::io;
139 ///
140 /// #[tokio::main]
141 /// async fn main() -> io::Result<()> {
142 /// let file = OpenOptions::new()
143 /// .write(true)
144 /// .open("foo.txt")
145 /// .await?;
146 ///
147 /// Ok(())
148 /// }
149 /// ```
150 pub fn write(&mut self, write: bool) -> &mut OpenOptions {
151 self.0.write(write);
152 self
153 }
154
155 /// Sets the option for the append mode.
156 ///
157 /// This option, when true, means that writes will append to a file instead
158 /// of overwriting previous contents. Note that setting
159 /// `.write(true).append(true)` has the same effect as setting only
160 /// `.append(true)`.
161 ///
162 /// For most filesystems, the operating system guarantees that all writes are
163 /// atomic: no writes get mangled because another process writes at the same
164 /// time.
165 ///
166 /// One maybe obvious note when using append-mode: make sure that all data
167 /// that belongs together is written to the file in one operation. This
168 /// can be done by concatenating strings before passing them to [`write()`],
169 /// or using a buffered writer (with a buffer of adequate size),
170 /// and calling [`flush()`] when the message is complete.
171 ///
172 /// If a file is opened with both read and append access, beware that after
173 /// opening, and after every write, the position for reading may be set at the
174 /// end of the file. So, before writing, save the current position (using
175 /// [`seek`]`(`[`SeekFrom`]`::`[`Current`]`(0))`), and restore it before the next read.
176 ///
177 /// This is an async version of [`std::fs::OpenOptions::append`][std]
178 ///
179 /// [std]: std::fs::OpenOptions::append
180 ///
181 /// ## Note
182 ///
183 /// This function doesn't create the file if it doesn't exist. Use the [`create`]
184 /// method to do so.
185 ///
186 /// [`write()`]: crate::io::AsyncWriteExt::write
187 /// [`flush()`]: crate::io::AsyncWriteExt::flush
188 /// [`seek`]: crate::io::AsyncSeekExt::seek
189 /// [`SeekFrom`]: std::io::SeekFrom
190 /// [`Current`]: std::io::SeekFrom::Current
191 /// [`create`]: OpenOptions::create
192 ///
193 /// # Examples
194 ///
195 /// ```no_run
196 /// use tokio::fs::OpenOptions;
197 /// use std::io;
198 ///
199 /// #[tokio::main]
200 /// async fn main() -> io::Result<()> {
201 /// let file = OpenOptions::new()
202 /// .append(true)
203 /// .open("foo.txt")
204 /// .await?;
205 ///
206 /// Ok(())
207 /// }
208 /// ```
209 pub fn append(&mut self, append: bool) -> &mut OpenOptions {
210 self.0.append(append);
211 self
212 }
213
214 /// Sets the option for truncating a previous file.
215 ///
216 /// If a file is successfully opened with this option set it will truncate
217 /// the file to 0 length if it already exists.
218 ///
219 /// The file must be opened with write access for truncate to work.
220 ///
221 /// This is an async version of [`std::fs::OpenOptions::truncate`][std]
222 ///
223 /// [std]: std::fs::OpenOptions::truncate
224 ///
225 /// # Examples
226 ///
227 /// ```no_run
228 /// use tokio::fs::OpenOptions;
229 /// use std::io;
230 ///
231 /// #[tokio::main]
232 /// async fn main() -> io::Result<()> {
233 /// let file = OpenOptions::new()
234 /// .write(true)
235 /// .truncate(true)
236 /// .open("foo.txt")
237 /// .await?;
238 ///
239 /// Ok(())
240 /// }
241 /// ```
242 pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
243 self.0.truncate(truncate);
244 self
245 }
246
247 /// Sets the option for creating a new file.
248 ///
249 /// This option indicates whether a new file will be created if the file
250 /// does not yet already exist.
251 ///
252 /// In order for the file to be created, [`write`] or [`append`] access must
253 /// be used.
254 ///
255 /// This is an async version of [`std::fs::OpenOptions::create`][std]
256 ///
257 /// [std]: std::fs::OpenOptions::create
258 /// [`write`]: OpenOptions::write
259 /// [`append`]: OpenOptions::append
260 ///
261 /// # Examples
262 ///
263 /// ```no_run
264 /// use tokio::fs::OpenOptions;
265 /// use std::io;
266 ///
267 /// #[tokio::main]
268 /// async fn main() -> io::Result<()> {
269 /// let file = OpenOptions::new()
270 /// .write(true)
271 /// .create(true)
272 /// .open("foo.txt")
273 /// .await?;
274 ///
275 /// Ok(())
276 /// }
277 /// ```
278 pub fn create(&mut self, create: bool) -> &mut OpenOptions {
279 self.0.create(create);
280 self
281 }
282
283 /// Sets the option to always create a new file.
284 ///
285 /// This option indicates whether a new file will be created. No file is
286 /// allowed to exist at the target location, also no (dangling) symlink.
287 ///
288 /// This option is useful because it is atomic. Otherwise between checking
289 /// whether a file exists and creating a new one, the file may have been
290 /// created by another process (a TOCTOU race condition / attack).
291 ///
292 /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are
293 /// ignored.
294 ///
295 /// The file must be opened with write or append access in order to create a
296 /// new file.
297 ///
298 /// This is an async version of [`std::fs::OpenOptions::create_new`][std]
299 ///
300 /// [std]: std::fs::OpenOptions::create_new
301 /// [`.create()`]: OpenOptions::create
302 /// [`.truncate()`]: OpenOptions::truncate
303 ///
304 /// # Examples
305 ///
306 /// ```no_run
307 /// use tokio::fs::OpenOptions;
308 /// use std::io;
309 ///
310 /// #[tokio::main]
311 /// async fn main() -> io::Result<()> {
312 /// let file = OpenOptions::new()
313 /// .write(true)
314 /// .create_new(true)
315 /// .open("foo.txt")
316 /// .await?;
317 ///
318 /// Ok(())
319 /// }
320 /// ```
321 pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
322 self.0.create_new(create_new);
323 self
324 }
325
326 /// Opens a file at `path` with the options specified by `self`.
327 ///
328 /// This is an async version of [`std::fs::OpenOptions::open`][std]
329 ///
330 /// [std]: std::fs::OpenOptions::open
331 ///
332 /// # Errors
333 ///
334 /// This function will return an error under a number of different
335 /// circumstances. Some of these error conditions are listed here, together
336 /// with their [`ErrorKind`]. The mapping to [`ErrorKind`]s is not part of
337 /// the compatibility contract of the function, especially the `Other` kind
338 /// might change to more specific kinds in the future.
339 ///
340 /// * [`NotFound`]: The specified file does not exist and neither `create`
341 /// or `create_new` is set.
342 /// * [`NotFound`]: One of the directory components of the file path does
343 /// not exist.
344 /// * [`PermissionDenied`]: The user lacks permission to get the specified
345 /// access rights for the file.
346 /// * [`PermissionDenied`]: The user lacks permission to open one of the
347 /// directory components of the specified path.
348 /// * [`AlreadyExists`]: `create_new` was specified and the file already
349 /// exists.
350 /// * [`InvalidInput`]: Invalid combinations of open options (truncate
351 /// without write access, no access mode set, etc.).
352 /// * [`Other`]: One of the directory components of the specified file path
353 /// was not, in fact, a directory.
354 /// * [`Other`]: Filesystem-level errors: full disk, write permission
355 /// requested on a read-only file system, exceeded disk quota, too many
356 /// open files, too long filename, too many symbolic links in the
357 /// specified path (Unix-like systems only), etc.
358 ///
359 /// # Examples
360 ///
361 /// ```no_run
362 /// use tokio::fs::OpenOptions;
363 /// use std::io;
364 ///
365 /// #[tokio::main]
366 /// async fn main() -> io::Result<()> {
367 /// let file = OpenOptions::new().open("foo.txt").await?;
368 /// Ok(())
369 /// }
370 /// ```
371 ///
372 /// [`ErrorKind`]: std::io::ErrorKind
373 /// [`AlreadyExists`]: std::io::ErrorKind::AlreadyExists
374 /// [`InvalidInput`]: std::io::ErrorKind::InvalidInput
375 /// [`NotFound`]: std::io::ErrorKind::NotFound
376 /// [`Other`]: std::io::ErrorKind::Other
377 /// [`PermissionDenied`]: std::io::ErrorKind::PermissionDenied
378 pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
379 let path = path.as_ref().to_owned();
380 let opts = self.0.clone();
381
382 let std = asyncify(move || opts.open(path)).await?;
383 Ok(File::from_std(std))
384 }
385}
386
387impl From<std::fs::OpenOptions> for OpenOptions {
388 fn from(options: std::fs::OpenOptions) -> OpenOptions {
389 OpenOptions(options)
390 }
391}
392
393impl Default for OpenOptions {
394 fn default() -> Self {
395 Self::new()
396 }
397}