diff --git a/tokio-fs/src/file/clone.rs b/tokio-fs/src/file/clone.rs new file mode 100644 index 000000000..9aaaf0870 --- /dev/null +++ b/tokio-fs/src/file/clone.rs @@ -0,0 +1,37 @@ +use super::File; + +use futures::{Future, Poll}; + +use std::io; + +/// Future returned by `File::try_clone`. +/// +/// Clones a file handle into two file handles. +/// +/// # Panics +/// +/// Will panic if polled after returning an item or error. +#[derive(Debug)] +pub struct CloneFuture { + file: Option, +} + +impl CloneFuture { + pub(crate) fn new(file: File) -> Self { + Self { file: Some(file) } + } +} + +impl Future for CloneFuture { + type Item = (File, File); + type Error = (File, io::Error); + + fn poll(&mut self) -> Poll { + self.file + .as_mut() + .expect("Cannot poll `CloneFuture` after it resolves") + .poll_try_clone() + .map(|inner| inner.map(|cloned| (self.file.take().unwrap(), cloned))) + .map_err(|err| (self.file.take().unwrap(), err)) + } +} diff --git a/tokio-fs/src/file/mod.rs b/tokio-fs/src/file/mod.rs index d31c973be..7ab533cd7 100644 --- a/tokio-fs/src/file/mod.rs +++ b/tokio-fs/src/file/mod.rs @@ -2,12 +2,14 @@ //! //! [`File`]: file/struct.File.html +mod clone; mod create; mod metadata; mod open; mod open_options; mod seek; +pub use self::clone::CloneFuture; pub use self::create::CreateFuture; pub use self::metadata::MetadataFuture; pub use self::open::OpenFuture; @@ -407,6 +409,38 @@ impl File { }) } + /// Create a new `File` instance that shares the same underlying file handle + /// as the existing `File` instance. Reads, writes, and seeks will affect both + /// File instances simultaneously. + /// + /// # Examples + /// ```no_run + /// # extern crate tokio; + /// use tokio::prelude::Future; + /// + /// fn main() { + /// let task = tokio::fs::File::create("foo.txt") + /// .and_then(|file| { + /// file.try_clone() + /// .map(|(file, clone)| { + /// // do something with the file and the clone + /// # println!("{:?} {:?}", file, clone); + /// }) + /// .map_err(|(file, err)| { + /// // you get the original file back if there's an error + /// # println!("{:?}", file); + /// err + /// }) + /// }) + /// .map_err(|err| eprintln!("IO error: {:?}", err)); + /// + /// tokio::run(task); + /// } + /// ``` + pub fn try_clone(self) -> CloneFuture { + CloneFuture::new(self) + } + /// Changes the permissions on the underlying file. /// /// # Platform-specific behavior diff --git a/tokio-fs/tests/file.rs b/tokio-fs/tests/file.rs index a12283a2f..0780dd3bf 100644 --- a/tokio-fs/tests/file.rs +++ b/tokio-fs/tests/file.rs @@ -125,3 +125,35 @@ fn seek() { }) }); } + +#[test] +fn clone() { + let dir = TmpBuilder::new() + .prefix("tokio-fs-tests") + .tempdir() + .unwrap(); + let file_path = dir.path().join("clone.txt"); + + pool::run( + File::create(file_path.clone()) + .and_then(|file| { + file.try_clone() + .map_err(|(_file, err)| err) + .and_then(|(file, clone)| { + io::write_all(file, "clone ") + .and_then(|_| io::write_all(clone, "successful")) + }) + }) + .then(|res| { + let _ = res.unwrap(); + Ok(()) + }), + ); + + let mut file = StdFile::open(&file_path).unwrap(); + + let mut dst = vec![]; + file.read_to_end(&mut dst).unwrap(); + + assert_eq!(dst, b"clone successful") +}