tokio_fs/file/
mod.rs

1//! Types for working with [`File`].
2//!
3//! [`File`]: file/struct.File.html
4
5mod clone;
6mod create;
7mod metadata;
8mod open;
9mod open_options;
10mod seek;
11
12pub use self::clone::CloneFuture;
13pub use self::create::CreateFuture;
14pub use self::metadata::MetadataFuture;
15pub use self::open::OpenFuture;
16pub use self::open_options::OpenOptions;
17pub use self::seek::SeekFuture;
18
19use tokio_io::{AsyncRead, AsyncWrite};
20
21use futures::Poll;
22
23use std::fs::{File as StdFile, Metadata, Permissions};
24use std::io::{self, Read, Seek, Write};
25use std::path::Path;
26
27/// A reference to an open file on the filesystem.
28///
29/// This is a specialized version of [`std::fs::File`][std] for usage from the
30/// Tokio runtime.
31///
32/// An instance of a `File` can be read and/or written depending on what options
33/// it was opened with. Files also implement Seek to alter the logical cursor
34/// that the file contains internally.
35///
36/// Files are automatically closed when they go out of scope.
37///
38/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
39///
40/// # Examples
41///
42/// Create a new file and asynchronously write bytes to it:
43///
44/// ```no_run
45/// extern crate tokio;
46///
47/// use tokio::prelude::{AsyncWrite, Future};
48///
49/// fn main() {
50///     let task = tokio::fs::File::create("foo.txt")
51///         .and_then(|mut file| file.poll_write(b"hello, world!"))
52///         .map(|res| {
53///             println!("{:?}", res);
54///         }).map_err(|err| eprintln!("IO error: {:?}", err));
55///
56///     tokio::run(task);
57/// }
58/// ```
59///
60/// Read the contents of a file into a buffer
61///
62/// ```no_run
63/// extern crate tokio;
64///
65/// use tokio::prelude::{AsyncRead, Future};
66///
67/// fn main() {
68///     let task = tokio::fs::File::open("foo.txt")
69///         .and_then(|mut file| {
70///             let mut contents = vec![];
71///             file.read_buf(&mut contents)
72///                 .map(|res| {
73///                     println!("{:?}", res);
74///                 })
75///         }).map_err(|err| eprintln!("IO error: {:?}", err));
76///     tokio::run(task);
77/// }
78/// ```
79#[derive(Debug)]
80pub struct File {
81    std: Option<StdFile>,
82}
83
84impl File {
85    /// Attempts to open a file in read-only mode.
86    ///
87    /// See [`OpenOptions`] for more details.
88    ///
89    /// [`OpenOptions`]: struct.OpenOptions.html
90    ///
91    /// # Errors
92    ///
93    /// `OpenFuture` results in an error if called from outside of the Tokio
94    /// runtime or if the underlying [`open`] call results in an error.
95    ///
96    /// [`open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.open
97    ///
98    /// # Examples
99    ///
100    /// ```no_run
101    /// # extern crate tokio;
102    /// use tokio::prelude::Future;
103    /// fn main() {
104    ///     let task = tokio::fs::File::open("foo.txt").and_then(|file| {
105    ///         // do something with the file ...
106    ///         file.metadata().map(|md| println!("{:?}", md))
107    ///     }).map_err(|e| {
108    ///         // handle errors
109    ///         eprintln!("IO error: {:?}", e);
110    ///     });
111    ///     tokio::run(task);
112    /// }
113    /// ```
114    pub fn open<P>(path: P) -> OpenFuture<P>
115    where
116        P: AsRef<Path> + Send + 'static,
117    {
118        OpenOptions::new().read(true).open(path)
119    }
120
121    /// Opens a file in write-only mode.
122    ///
123    /// This function will create a file if it does not exist, and will truncate
124    /// it if it does.
125    ///
126    /// See [`OpenOptions`] for more details.
127    ///
128    /// [`OpenOptions`]: struct.OpenOptions.html
129    ///
130    /// # Errors
131    ///
132    /// `CreateFuture` results in an error if called from outside of the Tokio
133    /// runtime or if the underlying [`create`] call results in an error.
134    ///
135    /// [`create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create
136    ///
137    /// # Examples
138    ///
139    /// ```no_run
140    /// # extern crate tokio;
141    /// use tokio::prelude::Future;
142    /// fn main() {
143    ///     let task = tokio::fs::File::create("foo.txt")
144    ///         .and_then(|file| {
145    ///             // do something with the created file ...
146    ///             file.metadata().map(|md| println!("{:?}", md))
147    ///         }).map_err(|e| {
148    ///             // handle errors
149    ///             eprintln!("IO error: {:?}", e);
150    ///     });
151    ///     tokio::run(task);
152    /// }
153    /// ```
154    pub fn create<P>(path: P) -> CreateFuture<P>
155    where
156        P: AsRef<Path> + Send + 'static,
157    {
158        CreateFuture::new(path)
159    }
160
161    /// Convert a [`std::fs::File`][std] to a [`tokio_fs::File`][file].
162    ///
163    /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
164    /// [file]: struct.File.html
165    ///
166    /// Examples
167    /// ```no_run
168    /// # extern crate tokio;
169    /// use std::fs::File;
170    ///
171    /// fn main() {
172    ///     let std_file = File::open("foo.txt").unwrap();
173    ///     let file = tokio::fs::File::from_std(std_file);
174    /// }
175    /// ```
176    pub fn from_std(std: StdFile) -> File {
177        File { std: Some(std) }
178    }
179
180    /// Seek to an offset, in bytes, in a stream.
181    ///
182    /// A seek beyond the end of a stream is allowed, but implementation
183    /// defined.
184    ///
185    /// If the seek operation completed successfully, this method returns the
186    /// new position from the start of the stream. That position can be used
187    /// later with `SeekFrom::Start`.
188    ///
189    /// # Errors
190    ///
191    /// Seeking to a negative offset is considered an error.
192    ///
193    /// # Examples
194    ///
195    /// ```no_run
196    /// # extern crate tokio;
197    /// use tokio::prelude::Future;
198    /// use std::io::SeekFrom;
199    ///
200    /// fn main() {
201    ///     let task = tokio::fs::File::open("foo.txt")
202    ///         // move cursor 6 bytes from the start of the file
203    ///         .and_then(|mut file| file.poll_seek(SeekFrom::Start(6)))
204    ///         .map(|res| {
205    ///             println!("{:?}", res);
206    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
207    ///
208    ///     tokio::run(task);
209    /// }
210    /// ```
211    pub fn poll_seek(&mut self, pos: io::SeekFrom) -> Poll<u64, io::Error> {
212        ::blocking_io(|| self.std().seek(pos))
213    }
214
215    /// Seek to an offset, in bytes, in a stream.
216    ///
217    /// Similar to `poll_seek`, but returning a `Future`.
218    ///
219    /// This method consumes the `File` and returns it back when the future
220    /// completes.
221    ///
222    /// # Examples
223    ///
224    /// ```no_run
225    /// # extern crate tokio;
226    /// use tokio::prelude::Future;
227    /// use std::io::SeekFrom;
228    ///
229    /// fn main() {
230    ///     let task = tokio::fs::File::create("foo.txt")
231    ///         .and_then(|file| file.seek(SeekFrom::Start(6)))
232    ///         .map(|file| {
233    ///             // handle returned file ..
234    ///             # println!("{:?}", file);
235    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
236    ///
237    ///     tokio::run(task);
238    /// }
239    /// ```
240    pub fn seek(self, pos: io::SeekFrom) -> SeekFuture {
241        SeekFuture::new(self, pos)
242    }
243
244    /// Attempts to sync all OS-internal metadata to disk.
245    ///
246    /// This function will attempt to ensure that all in-core data reaches the
247    /// filesystem before returning.
248    ///
249    /// # Examples
250    ///
251    /// ```no_run
252    /// # extern crate tokio;
253    /// use tokio::prelude::{AsyncWrite, Future};
254    ///
255    /// fn main() {
256    ///     let task = tokio::fs::File::create("foo.txt")
257    ///         .and_then(|mut file| {
258    ///             file.poll_write(b"hello, world!")?;
259    ///             file.poll_sync_all()
260    ///         })
261    ///         .map(|res| {
262    ///             // handle returned result ..
263    ///             # println!("{:?}", res);
264    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
265    ///
266    ///     tokio::run(task);
267    /// }
268    /// ```
269    pub fn poll_sync_all(&mut self) -> Poll<(), io::Error> {
270        ::blocking_io(|| self.std().sync_all())
271    }
272
273    /// This function is similar to `poll_sync_all`, except that it may not
274    /// synchronize file metadata to the filesystem.
275    ///
276    /// This is intended for use cases that must synchronize content, but don't
277    /// need the metadata on disk. The goal of this method is to reduce disk
278    /// operations.
279    ///
280    /// Note that some platforms may simply implement this in terms of `poll_sync_all`.
281    ///
282    /// # Examples
283    ///
284    /// ```no_run
285    /// # extern crate tokio;
286    /// use tokio::prelude::{AsyncWrite, Future};
287    ///
288    /// fn main() {
289    ///     let task = tokio::fs::File::create("foo.txt")
290    ///         .and_then(|mut file| {
291    ///             file.poll_write(b"hello, world!")?;
292    ///             file.poll_sync_data()
293    ///         })
294    ///         .map(|res| {
295    ///             // handle returned result ..
296    ///             # println!("{:?}", res);
297    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
298    ///
299    ///     tokio::run(task);
300    /// }
301    /// ```
302    pub fn poll_sync_data(&mut self) -> Poll<(), io::Error> {
303        ::blocking_io(|| self.std().sync_data())
304    }
305
306    /// Truncates or extends the underlying file, updating the size of this file to become size.
307    ///
308    /// If the size is less than the current file's size, then the file will be
309    /// shrunk. If it is greater than the current file's size, then the file
310    /// will be extended to size and have all of the intermediate data filled in
311    /// with 0s.
312    ///
313    /// # Errors
314    ///
315    /// This function will return an error if the file is not opened for
316    /// writing.
317    ///
318    /// # Examples
319    ///
320    /// ```no_run
321    /// # extern crate tokio;
322    /// use tokio::prelude::Future;
323    ///
324    /// fn main() {
325    ///     let task = tokio::fs::File::create("foo.txt")
326    ///         .and_then(|mut file| {
327    ///             file.poll_set_len(10)
328    ///         })
329    ///         .map(|res| {
330    ///             // handle returned result ..
331    ///             # println!("{:?}", res);
332    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
333    ///
334    ///     tokio::run(task);
335    /// }
336    /// ```
337    pub fn poll_set_len(&mut self, size: u64) -> Poll<(), io::Error> {
338        ::blocking_io(|| self.std().set_len(size))
339    }
340
341    /// Queries metadata about the underlying file.
342    ///
343    /// # Examples
344    ///
345    /// ```no_run
346    /// # extern crate tokio;
347    /// use tokio::prelude::Future;
348    ///
349    /// fn main() {
350    ///     let task = tokio::fs::File::create("foo.txt")
351    ///         .and_then(|file| file.metadata())
352    ///         .map(|metadata| {
353    ///             println!("{:?}", metadata);
354    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
355    ///
356    ///     tokio::run(task);
357    /// }
358    /// ```
359    pub fn metadata(self) -> MetadataFuture {
360        MetadataFuture::new(self)
361    }
362
363    /// Queries metadata about the underlying file.
364    ///
365    /// # Examples
366    ///
367    /// ```no_run
368    /// # extern crate tokio;
369    /// use tokio::prelude::Future;
370    ///
371    /// fn main() {
372    ///     let task = tokio::fs::File::create("foo.txt")
373    ///         .and_then(|mut file| file.poll_metadata())
374    ///         .map(|metadata| {
375    ///             // metadata is of type Async::Ready<Metadata>
376    ///             println!("{:?}", metadata);
377    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
378    ///
379    ///     tokio::run(task);
380    /// }
381    /// ```
382    pub fn poll_metadata(&mut self) -> Poll<Metadata, io::Error> {
383        ::blocking_io(|| self.std().metadata())
384    }
385
386    /// Create a new `File` instance that shares the same underlying file handle
387    /// as the existing `File` instance. Reads, writes, and seeks will affect both
388    /// File instances simultaneously.
389    ///
390    /// # Examples
391    ///
392    /// ```no_run
393    /// # extern crate tokio;
394    /// use tokio::prelude::Future;
395    ///
396    /// fn main() {
397    ///     let task = tokio::fs::File::create("foo.txt")
398    ///         .and_then(|mut file| file.poll_try_clone())
399    ///         .map(|clone| {
400    ///             // do something with the clone
401    ///             # println!("{:?}", clone);
402    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
403    ///
404    ///     tokio::run(task);
405    /// }
406    /// ```
407    pub fn poll_try_clone(&mut self) -> Poll<File, io::Error> {
408        ::blocking_io(|| {
409            let std = self.std().try_clone()?;
410            Ok(File::from_std(std))
411        })
412    }
413
414    /// Create a new `File` instance that shares the same underlying file handle
415    /// as the existing `File` instance. Reads, writes, and seeks will affect both
416    /// File instances simultaneously.
417    ///
418    /// # Examples
419    /// ```no_run
420    /// # extern crate tokio;
421    /// use tokio::prelude::Future;
422    ///
423    /// fn main() {
424    ///     let task = tokio::fs::File::create("foo.txt")
425    ///         .and_then(|file| {
426    ///             file.try_clone()
427    ///                 .map(|(file, clone)| {
428    ///                     // do something with the file and the clone
429    ///                     # println!("{:?} {:?}", file, clone);
430    ///                 })
431    ///                 .map_err(|(file, err)| {
432    ///                     // you get the original file back if there's an error
433    ///                     # println!("{:?}", file);
434    ///                     err
435    ///                 })
436    ///         })
437    ///         .map_err(|err| eprintln!("IO error: {:?}", err));
438    ///
439    ///     tokio::run(task);
440    /// }
441    /// ```
442    pub fn try_clone(self) -> CloneFuture {
443        CloneFuture::new(self)
444    }
445
446    /// Changes the permissions on the underlying file.
447    ///
448    /// # Platform-specific behavior
449    ///
450    /// This function currently corresponds to the `fchmod` function on Unix and
451    /// the `SetFileInformationByHandle` function on Windows. Note that, this
452    /// [may change in the future][changes].
453    ///
454    /// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
455    ///
456    /// # Errors
457    ///
458    /// This function will return an error if the user lacks permission change
459    /// attributes on the underlying file. It may also return an error in other
460    /// os-specific unspecified cases.
461    ///
462    /// # Examples
463    ///
464    /// ```no_run
465    /// # extern crate tokio;
466    /// use tokio::prelude::Future;
467    ///
468    /// fn main() {
469    ///     let task = tokio::fs::File::create("foo.txt")
470    ///         .and_then(|file| file.metadata())
471    ///         .map(|(mut file, metadata)| {
472    ///             let mut perms = metadata.permissions();
473    ///             perms.set_readonly(true);
474    ///             match file.poll_set_permissions(perms) {
475    ///                 Err(e) => eprintln!("{}", e),
476    ///                 _ => println!("permissions set!"),
477    ///             }
478    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
479    ///
480    ///     tokio::run(task);
481    /// }
482    /// ```
483    pub fn poll_set_permissions(&mut self, perm: Permissions) -> Poll<(), io::Error> {
484        ::blocking_io(|| self.std().set_permissions(perm))
485    }
486
487    /// Destructures the `tokio_fs::File` into a [`std::fs::File`][std].
488    ///
489    /// # Panics
490    ///
491    /// This function will panic if `shutdown` has been called.
492    ///
493    /// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
494    ///
495    /// # Examples
496    ///
497    /// ```no_run
498    /// # extern crate tokio;
499    /// use tokio::prelude::Future;
500    ///
501    /// fn main() {
502    ///     let task = tokio::fs::File::create("foo.txt")
503    ///         .map(|file| {
504    ///             let std_file = file.into_std();
505    ///             // do something with the std::fs::File
506    ///             # println!("{:?}", std_file);
507    ///         }).map_err(|err| eprintln!("IO error: {:?}", err));
508    ///
509    ///     tokio::run(task);
510    /// }
511    /// ```
512    pub fn into_std(mut self) -> StdFile {
513        self.std.take().expect("`File` instance already shutdown")
514    }
515
516    fn std(&mut self) -> &mut StdFile {
517        self.std.as_mut().expect("`File` instance already shutdown")
518    }
519}
520
521impl Read for File {
522    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
523        ::would_block(|| self.std().read(buf))
524    }
525}
526
527impl AsyncRead for File {
528    unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
529        false
530    }
531}
532
533impl Write for File {
534    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
535        ::would_block(|| self.std().write(buf))
536    }
537
538    fn flush(&mut self) -> io::Result<()> {
539        ::would_block(|| self.std().flush())
540    }
541}
542
543impl AsyncWrite for File {
544    fn shutdown(&mut self) -> Poll<(), io::Error> {
545        ::blocking_io(|| {
546            self.std = None;
547            Ok(())
548        })
549    }
550}
551
552impl Drop for File {
553    fn drop(&mut self) {
554        if let Some(_std) = self.std.take() {
555            // This is probably fine as closing a file *shouldn't* be a blocking
556            // operation. That said, ideally `shutdown` is called first.
557        }
558    }
559}