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}