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}