Repository: Stebalien/tempfile
Pull request #33 (2017-09-12 03:57:39)

Merge tempdir api
Combines the [`tempdir`](https://github.com/rust-lang-nursery/tempdir) API, beefs out the docs, and refactors `NamedTempFile` a bit.

If you'd rather I didn't refactor the internal structure then I can rejig this PR to minimise file movement.

Some previous discussion/iteration on combining the APIs: https://github.com/KodrAus/tempfile/pull/1

cc: @Stebalien @alexcrichton

KodrAus: @Stebalien you'll probably want to take a thorough look through the docs I've added, I might've gotten some security notices wrong.
KodrAus: Support for `1.13.0` needs a [quick change in `remove_dir_all`](https://github.com/Aaronepower/remove_dir_all/pull/2)
Stebalien: I will review this ASAP but that probably won't be till the weekend.
KodrAus: Friendly ping @Stebalien to take a look at this when you've got bandwidth. No pressure though.

We could move forward without that fix in `remove_dir_all` but it would mean bumping the minimum rust version supported.
opilar: A little question: when `tempdir` and `tempfile` will merge apis, should we rename the crate to `temp`?
KodrAus: @opilar the idea is to keep the `tempfile` crate name.
Stebalien: @KodrAus sorry, my family ended up visiting that weekend and now I'm traveling but I'll try to get to this in the next few days.
Stebalien: I'm still alive, FYI (just busy). Sorry.
KodrAus: @stebalien that's no problem :) We're not working on a tight timeline here so there's no hurry, no pressure. I just remembered there was a new version of `remove_dir_all` I could use.
azriel91: ping! I came across this when looking to write tests that use a temporary directory. Is this PR close to being merged, or shall I use `tempdir`?
KodrAus: @azriel91 sorry I just got a chance to loop back here. I would use `tempdir`. We'll have a nice smooth upgrade path when the APIs are merged, but this PR is only one step of that process.
KodrAus: Thanks for the thorough review @Stebalien! I'll rebase and work through the changes over the next week :+1:

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 56e7002..c4d8bb6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,8 +1,10 @@
 [package]
 name = "tempfile"
 version = "2.2.0"
-authors = ["Steven Allen <steven@stebalien.com>"]
-description = "Securely create temporary files."
+authors = ["Steven Allen <steven@stebalien.com>", "The Rust Project Developers"]
+description = """
+A library for managing temporary files and directories.
+"""
 
 documentation = "https://stebalien.github.io/tempfile/tempfile/"
 repository = "https://github.com/Stebalien/tempfile"
@@ -12,6 +14,7 @@ license = "MIT/Apache-2.0"
 
 [dependencies]
 rand = "0.4"
+remove_dir_all = "0.3"
 
 [target.'cfg(unix)'.dependencies]
 libc = "0.2"
diff --git a/src/dir.rs b/src/dir.rs
new file mode 100644
index 0000000..bda0f31
--- /dev/null
+++ b/src/dir.rs
@@ -0,0 +1,407 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::{io, fmt, fs};
+use std::path::{self, PathBuf, Path};
+use remove_dir_all::remove_dir_all;
+
+use ::Builder;
+
+/// Create a new temporary directory.
+///
+/// The `tempdir` function creates a directory in the file system
+/// and returns a [`TempDir`].
+/// The directory will be automatically deleted when the `TempDir`s
+/// desctructor is run.
+///
+/// # Resource Leaking
+///
+/// See [the resource leaking][resource-leaking] docs on `TempDir`.
+///
+/// # Errors
+///
+/// If the directory can not be created, `Err` is returned.
+///
+/// # Examples
+///
+/// ```
+/// # extern crate tempfile;
+/// use tempfile::tempdir;
+/// use std::fs::File;
+/// use std::io::{self, Write};
+///
+/// # fn main() {
+/// #     if let Err(_) = run() {
+/// #         ::std::process::exit(1);
+/// #     }
+/// # }
+/// # fn run() -> Result<(), io::Error> {
+/// // Create a directory inside of `std::env::temp_dir()`
+/// let dir = tempdir()?;
+///
+/// let file_path = dir.path().join("my-temporary-note.txt");
+/// let mut file = File::create(file_path)?;
+/// writeln!(file, "Brian was here. Briefly.")?;
+///
+/// // `tmp_dir` goes out of scope, the directory as well as
+/// // `tmp_file` will be deleted here.
+/// drop(file);
+/// dir.close()?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`TempDir`]: struct.TempDir.html
+/// [resource-leaking]: struct.TempDir.html#resource-leaking
+pub fn tempdir() -> io::Result<TempDir> {
+    TempDir::new()
+}
+
+/// Create a new temporary directory.
+///
+/// The `tempdir` function creates a directory in the file system
+/// and returns a [`TempDir`].
+/// The directory will be automatically deleted when the `TempDir`s
+/// desctructor is run.
+///
+/// # Resource Leaking
+///
+/// See [the resource leaking][resource-leaking] docs on `TempDir`.
+///
+/// # Errors
+///
+/// If the directory can not be created, `Err` is returned.
+///
+/// # Examples
+///
+/// ```
+/// # extern crate tempfile;
+/// use tempfile::tempdir;
+/// use std::fs::File;
+/// use std::io::{self, Write};
+///
+/// # fn main() {
+/// #     if let Err(_) = run() {
+/// #         ::std::process::exit(1);
+/// #     }
+/// # }
+/// # fn run() -> Result<(), io::Error> {
+/// // Create a directory inside of `std::env::temp_dir()`,
+/// let dir = tempdir()?;
+///
+/// let file_path = dir.path().join("my-temporary-note.txt");
+/// let mut file = File::create(file_path)?;
+/// writeln!(file, "Brian was here. Briefly.")?;
+///
+/// // `tmp_dir` goes out of scope, the directory as well as
+/// // `tmp_file` will be deleted here.
+/// drop(file);
+/// dir.close()?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`TempDir`]: struct.TempDir.html
+/// [resource-leaking]: struct.TempDir.html#resource-leaking
+pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
+    TempDir::new_in(dir)
+}
+
+/// A directory in the filesystem that is automatically deleted when
+/// it goes out of scope.
+///
+/// The [`TempDir`] type creates a directory on the file system that
+/// is deleted once it goes out of scope. At construction, the
+/// `TempDir` creates a new directory with a randomly generated name,
+/// and with a prefix of your choosing.
+///
+/// The default constructor, [`TempDir::new()`], creates directories in
+/// the location returned by [`std::env::temp_dir()`], but `TempDir`
+/// can be configured to manage a temporary directory in any location
+/// by constructing with a [`Builder`].
+///
+/// After creating a `TempDir`, work with the file system by doing
+/// standard [`std::fs`] file system operations on its [`Path`],
+/// which can be retrieved with [`TempDir::path()`]. Once the `TempDir`
+/// value is dropped, the directory at the path will be deleted, along
+/// with any files and directories it contains. It is your responsibility
+/// to ensure that no further file system operations are attempted
+/// inside the temporary directory once it has been deleted.
+///
+/// # Resource Leaking
+///
+/// Various platform-specific conditions may cause `TempDir` to fail
+/// to delete the underlying directory. It's important to ensure that
+/// handles (like [`File`] and [`ReadDir`]) to files inside the
+/// directory are dropped before the `TempDir` goes out of scope. The
+/// `TempDir` destructor will silently ignore any errors in deleting
+/// the directory; to instead handle errors call [`TempDir::close()`].
+///
+/// Note that if the program exits before the `TempDir` destructor is
+/// run, such as via [`std::process::exit()`], by segfaulting, or by
+/// receiving a signal like `SIGINT`, then the temporary directory
+/// will not be deleted.
+/// 
+/// # Examples
+///
+/// Create a temporary directory with a generated name:
+/// 
+/// ```
+/// use std::fs::File;
+/// use std::io::Write;
+/// use tempfile::TempDir;
+///
+/// # use std::io;
+/// # fn run() -> Result<(), io::Error> {
+/// // Create a directory inside of `std::env::temp_dir()`
+/// let tmp_dir = TempDir::new()?;
+/// # Ok(())
+/// # }
+/// ```
+/// 
+/// Create a temporary directory with a prefix in its name:
+/// 
+/// ```
+/// use std::fs::File;
+/// use std::io::Write;
+/// use tempfile::Builder;
+///
+/// # use std::io;
+/// # fn run() -> Result<(), io::Error> {
+/// // Create a directory inside of `std::env::temp_dir()`,
+/// // whose name will begin with 'example'.
+/// let tmp_dir = Builder::new().prefix("example").tempdir()?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html
+/// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
+/// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html
+/// [`Builder`]: struct.Builder.html
+/// [`TempDir::close()`]: struct.TempDir.html#method.close
+/// [`TempDir::new()`]: struct.TempDir.html#method.new
+/// [`TempDir::path()`]: struct.TempDir.html#method.path
+/// [`TempDir`]: struct.TempDir.html
+/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
+/// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html
+/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
+pub struct TempDir {
+    path: Option<PathBuf>,
+}
+
+impl TempDir {
+    /// Attempts to make a temporary directory inside of `env::temp_dir()`.
+    /// 
+    /// See [`Builder`] for more configuration.
+    /// 
+    /// The directory and everything inside it will be automatically deleted 
+    /// once the returned `TempDir` is destroyed.
+    ///
+    /// # Errors
+    ///
+    /// If the directory can not be created, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::fs::File;
+    /// use std::io::Write;
+    /// use tempfile::TempDir;
+    ///
+    /// # use std::io;
+    /// # fn run() -> Result<(), io::Error> {
+    /// // Create a directory inside of `std::env::temp_dir()`
+    /// let tmp_dir = TempDir::new()?;
+    /// 
+    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
+    /// let mut tmp_file = File::create(file_path)?;
+    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
+    ///
+    /// // `tmp_dir` goes out of scope, the directory as well as
+    /// // `tmp_file` will be deleted here.
+    /// # Ok(())
+    /// # }
+    /// ```
+    /// 
+    /// [`Builder`]: struct.Builder.html
+    pub fn new() -> io::Result<TempDir> {
+        Builder::new().tempdir()
+    }
+
+    /// Attempts to make a temporary directory inside of `tmpdir`
+    /// whose name will have the prefix `prefix`. The directory and
+    /// everything inside it will be automatically deleted once the
+    /// returned `TempDir` is destroyed.
+    ///
+    /// # Errors
+    ///
+    /// If the directory can not be created, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::fs::{self, File};
+    /// use std::io::Write;
+    /// use tempfile::TempDir;
+    ///
+    /// # use std::io;
+    /// # fn run() -> Result<(), io::Error> {
+    /// // Create a directory inside of the current directory
+    /// let tmp_dir = TempDir::new_in(".")?;
+    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
+    /// let mut tmp_file = File::create(file_path)?;
+    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
+        Builder::new().tempdir_in(dir)
+    }
+
+    /// Accesses the [`Path`] to the temporary directory.
+    ///
+    /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use tempfile::TempDir;
+    ///
+    /// # use std::io;
+    /// # fn run() -> Result<(), io::Error> {
+    /// let tmp_path;
+    ///
+    /// {
+    ///    let tmp_dir = TempDir::new()?;
+    ///    tmp_path = tmp_dir.path().to_owned();
+    ///
+    ///    // Check that the temp directory actually exists.
+    ///    assert!(tmp_path.exists());
+    ///
+    ///    // End of `tmp_dir` scope, directory will be deleted
+    /// }
+    ///
+    /// // Temp directory should be deleted by now
+    /// assert_eq!(tmp_path.exists(), false);
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn path(&self) -> &path::Path {
+        self.path.as_ref().unwrap()
+    }
+
+    /// Unwraps the [`Path`] contained in the `TempDir` and
+    /// returns it. This destroys the `TempDir` without deleting the
+    /// directory represented by the returned `Path`.
+    ///
+    /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::fs;
+    /// use tempfile::TempDir;
+    ///
+    /// # use std::io;
+    /// # fn run() -> Result<(), io::Error> {
+    /// let tmp_dir = TempDir::new()?;
+    ///
+    /// // Convert `tmp_dir` into a `Path`, destroying the `TempDir`
+    /// // without deleting the directory.
+    /// let tmp_path = tmp_dir.into_path();
+    ///
+    /// // Delete the temporary directory ourselves.
+    /// fs::remove_dir_all(tmp_path)?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn into_path(mut self) -> PathBuf {
+        self.path.take().unwrap()
+    }
+
+    /// Closes and removes the temporary directory, returing a `Result`.
+    ///
+    /// Although `TempDir` removes the directory on drop, in the destructor
+    /// any errors are ignored. To detect errors cleaning up the temporary
+    /// directory, call `close` instead.
+    ///
+    /// # Errors
+    ///
+    /// This function may return a variety of [`std::io::Error`]s that result from deleting
+    /// the files and directories contained with the temporary directory,
+    /// as well as from deleting the temporary directory itself. These errors
+    /// may be platform specific.
+    ///
+    /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::fs::File;
+    /// use std::io::Write;
+    /// use tempfile::TempDir;
+    ///
+    /// # use std::io;
+    /// # fn run() -> Result<(), io::Error> {
+    /// // Create a directory inside of `std::env::temp_dir()`, named with
+    /// // the prefix, "example".
+    /// let tmp_dir = TempDir::new()?;
+    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
+    /// let mut tmp_file = File::create(file_path)?;
+    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
+    ///
+    /// // By closing the `TempDir` explicitly we can check that it has
+    /// // been deleted successfully. If we don't close it explicitly,
+    /// // the directory will still be deleted when `tmp_dir` goes out
+    /// // of scope, but we won't know whether deleting the directory
+    /// // succeeded.
+    /// drop(tmp_file);
+    /// tmp_dir.close()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn close(mut self) -> io::Result<()> {
+        let result = remove_dir_all(self.path());
+
+        // Prevent the Drop impl from removing the dir a second time.
+        self.path = None;
+
+        result
+    }
+}
+
+impl AsRef<Path> for TempDir {
+    fn as_ref(&self) -> &Path {
+        self.path()
+    }
+}
+
+impl fmt::Debug for TempDir {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("TempDir")
+            .field("path", &self.path())
+            .finish()
+    }
+}
+
+impl Drop for TempDir {
+    fn drop(&mut self) {
+        // Path is `None` if `close()` or `into_path()` has been called.
+        if let Some(ref p) = self.path {
+            let _ =  remove_dir_all(p);
+        }
+    }
+}
+
+// pub(crate)
+pub fn create(path: PathBuf) -> io::Result<TempDir> {
+    fs::create_dir(&path).map(|_| TempDir { path: Some(path) })
+}
diff --git a/src/directory.rs b/src/directory.rs
deleted file mode 100644
index 8aeb73a..0000000
--- a/src/directory.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-use super::imp;
-use super::util;
-
-struct TempDir {
-    path: PathBuf,
-    inner: TempDirImp
-}
-
-impl TempDir {
-    pub fn new() -> TempDir {
-        // TODO
-    }
-    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
-        let path = dir.as_ref().join(&util::tmpname());
-        path.push(""); // Make it a directory.
-        // TODO
-    }
-
-    // TODO: Real Options
-    /// Securely create a file in this directory.
-    ///
-    /// TODO: SECURITY WARNING
-    pub fn open_file<P: AsRef<Path>>(&self, path: P, create: bool) -> io::Result<File> {
-        self.inner.open_file(path.as_ref(), create)
-    }
-
-    pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
-        self.inner.remove_file(path.as_ref());
-    }
-
-    /// Remove the specified directory.
-    ///
-    /// If recurse is true, this function recursivly deletes all files and directories under the
-    /// specified directory. When false, this function will refuse to delete non-empty directories.
-    ///
-    /// Note: If recurse is true, this function is not atomic. If it failes to delete a
-    /// subdirectory/file, this function will return an error without proceeding.
-    pub fn remove_dir<P: AsRef<Path>>(&self, path: P, recurse: bool) -> io::Result<()> {
-        self.inner.remove_dir(path.as_ref(), recurse);
-    }
-
-    pub fn rename<P1: AsRef<Path>, P2: AsRef<Path>>(&self, from: P1, to: P2) -> io::Result<()> {
-        self.inner.rename(from.as_ref(), to.as_ref());
-    }
-
-    /// Persist the temporary directory at the target path.
-    ///
-    /// If an empty directory exists at the target path, persist will atomically replace it. If
-    /// this method fails, it will return `self` in the resulting PersistError.
-    ///
-    /// Note: Temporary directories cannot be persisted across filesystems.
-    ///
-    /// *SECURITY WARNING:* Only use this method if you're positive that a temporary file cleaner
-    /// won't have deleted your directory. Otherwise, you might end up persisting an attacker
-    /// controlled directory.
-    #[inline]
-    pub fn persist<P: AsRef<Path>>(mut self, new_path: P) -> Result<File, PersistError> {
-        match fs::rename(&self.inner().path, new_path) {
-            Ok(_) => Ok(self.0.take().unwrap().file),
-            Err(e) => Err(PersistError { file: self, error: e }),
-        }
-    }
-}
diff --git a/src/imp/mod.rs b/src/file/imp/mod.rs
similarity index 100%
rename from src/imp/mod.rs
rename to src/file/imp/mod.rs
diff --git a/src/imp/unix.rs b/src/file/imp/unix.rs
similarity index 84%
rename from src/imp/unix.rs
rename to src/file/imp/unix.rs
index 6a0f803..aeebb1e 100644
--- a/src/imp/unix.rs
+++ b/src/file/imp/unix.rs
@@ -4,7 +4,7 @@ use std::os::unix::ffi::OsStrExt;
 use std::ffi::CString;
 use std::io;
 use std::os::unix::io::{RawFd, FromRawFd, AsRawFd};
-use std::fs::{self, File, OpenOptions};
+use std::fs::{File, OpenOptions};
 use std::path::Path;
 use util;
 
@@ -52,7 +52,7 @@ pub fn create_named(path: &Path) -> io::Result<File> {
 }
 
 #[cfg(target_os = "redox")]
-pub fn create_named(path: &Path) -> io::Result<File> {
+pub fn create_named(path: PathBuf) -> io::Result<File> {
     unsafe {
         let fd = try!(cvt_err(open(path.as_os_str().as_bytes(),
                                    O_CLOEXEC | O_EXCL | O_RDWR | O_CREAT | 0o600)));
@@ -80,21 +80,7 @@ pub fn create(dir: &Path) -> io::Result<File> {
 }
 
 fn create_unix(dir: &Path) -> io::Result<File> {
-    for _ in 0..::NUM_RETRIES {
-        let tmp_path = dir.join(util::tmpname(".tmp", "", ::NUM_RAND_CHARS));
-        return match create_named(&tmp_path) {
-            Ok(file) => {
-                // I should probably tell the user this failed but the temporary file creation
-                // didn't really fail...
-                let _ = fs::remove_file(tmp_path);
-                Ok(file)
-            }
-            Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
-            Err(e) => Err(e),
-        };
-    }
-    Err(io::Error::new(io::ErrorKind::AlreadyExists,
-                       "too many temporary directories already exist"))
+    util::create_helper(dir, ".tmp", "", ::NUM_RAND_CHARS, |path| create_named(&path))
 }
 
 unsafe fn stat(fd: RawFd) -> io::Result<stat_t> {
diff --git a/src/imp/windows.rs b/src/file/imp/windows.rs
similarity index 84%
rename from src/imp/windows.rs
rename to src/file/imp/windows.rs
index 89e16bf..f849402 100644
--- a/src/imp/windows.rs
+++ b/src/file/imp/windows.rs
@@ -61,22 +61,17 @@ pub fn create_named(path: &Path) -> io::Result<File> {
 }
 
 pub fn create(dir: &Path) -> io::Result<File> {
-    for _ in 0..::NUM_RETRIES {
-        return match win_create(&dir.join(&util::tmpname(".tmp", "", ::NUM_RAND_CHARS)),
-                                ACCESS,
-                                0, // Exclusive
-                                CREATE_NEW,
-                                FLAGS | FILE_FLAG_DELETE_ON_CLOSE) {
-            Ok(f) => Ok(f),
-            Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
-            Err(e) => Err(e),
-        };
-    }
-    Err(io::Error::new(io::ErrorKind::AlreadyExists,
-                       "too many temporary directories already exist"))
+    util::create_helper(dir, ".tmp", "", ::NUM_RAND_CHARS, |path| {
+        win_create(
+            &path,
+            ACCESS,
+            0, // Exclusive
+            CREATE_NEW,
+            FLAGS | FILE_FLAG_DELETE_ON_CLOSE)
+    })
 }
 
-pub fn reopen(file: &File, path: &Path) -> io::Result<File> {
+pub fn reopen(file: &File, _path: &Path) -> io::Result<File> {
     let handle = file.as_raw_handle();
     unsafe {
         let handle = ReOpenFile(handle as HANDLE, ACCESS, SHARE_MODE, 0);
diff --git a/src/file/mod.rs b/src/file/mod.rs
new file mode 100644
index 0000000..5b0ea59
--- /dev/null
+++ b/src/file/mod.rs
@@ -0,0 +1,563 @@
+use std::io::{self, Read, Write, Seek, SeekFrom};
+use std::fs::{self, File};
+use std::path::{Path, PathBuf};
+use std::error;
+use std::fmt;
+use std::env;
+use std;
+
+use ::Builder;
+
+mod imp;
+
+/// Create a new temporary file.
+///
+/// The file will be created in the location returned by [`std::env::temp_dir()`].
+///
+/// # Security
+///
+/// This variant is secure/reliable in the presence of a pathological temporary file cleaner.
+///
+/// # Resource Leaking
+///
+/// The temporary file will be automatically removed by the OS when the last handle to it is closed.
+/// This doesn't rely on Rust destructors being run, so will (almost) never fail to clean up the temporary file.
+///
+/// # Errors
+///
+/// If the file can not be created, `Err` is returned.
+///
+/// # Examples
+///
+/// ```
+/// # extern crate tempfile;
+/// use tempfile::tempfile;
+/// use std::io::{self, Write};
+///
+/// # fn main() {
+/// #     if let Err(_) = run() {
+/// #         ::std::process::exit(1);
+/// #     }
+/// # }
+/// # fn run() -> Result<(), io::Error> {
+/// // Create a file inside of `std::env::temp_dir()`.
+/// let mut file = tempfile()?;
+///
+/// writeln!(file, "Brian was here. Briefly.")?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
+pub fn tempfile() -> io::Result<File> {
+    tempfile_in(&env::temp_dir())
+}
+
+/// Create a new temporary file in the specified directory.
+///
+/// # Security
+///
+/// This variant is secure/reliable in the presence of a pathological temporary file cleaner.
+/// If the temporary file isn't created in [`std::env::temp_dir()`] then temporary file cleaners aren't an issue.
+///
+/// # Resource Leaking
+///
+/// The temporary file will be automatically removed by the OS when the last handle to it is closed.
+/// This doesn't rely on Rust destructors being run, so will (almost) never fail to clean up the temporary file.
+///
+/// # Errors
+///
+/// If the file can not be created, `Err` is returned.
+///
+/// # Examples
+///
+/// ```
+/// # extern crate tempfile;
+/// use tempfile::tempfile_in;
+/// use std::io::{self, Write};
+///
+/// # fn main() {
+/// #     if let Err(_) = run() {
+/// #         ::std::process::exit(1);
+/// #     }
+/// # }
+/// # fn run() -> Result<(), io::Error> {
+/// // Create a file inside of the current working directory
+/// let mut file = tempfile_in("./")?;
+///
+/// writeln!(file, "Brian was here. Briefly.")?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
+pub fn tempfile_in<P: AsRef<Path>>(dir: P) -> io::Result<File> {
+    imp::create(dir.as_ref())
+}
+
+/// A named temporary file.
+///
+/// The default constructor, [`NamedTempFile::new()`], creates files in
+/// the location returned by [`std::env::temp_dir()`], but `NamedTempFile`
+/// can be configured to manage a temporary file in any location
+/// by constructing with [`NamedTempFile::new_in()`].
+///
+/// # Security
+///
+/// This variant is *NOT* secure/reliable in the presence of a pathological temporary file cleaner.
+///
+/// # Resource Leaking
+///
+/// If the program exits before the `NamedTempFile` destructor is
+/// run, such as via [`std::process::exit()`], by segfaulting, or by
+/// receiving a signal like `SIGINT`, then the temporary file
+/// will not be deleted.
+///
+/// Use the [`tempfile()`] function unless you absolutely need a named file.
+///
+/// [`tempfile()`]: fn.tempfile.html
+/// [`NamedTempFile::new()`]: #method.new
+/// [`NamedTempFile::new_in()`]: #method.new_in
+/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
+/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
+pub struct NamedTempFile(Option<NamedTempFileInner>);
+
+struct NamedTempFileInner {
+    file: File,
+    path: PathBuf,
+}
+
+impl fmt::Debug for NamedTempFile {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "NamedTempFile({:?})", self.inner().path)
+    }
+}
+
+impl AsRef<Path> for NamedTempFile {
+    #[inline]
+    fn as_ref(&self) -> &Path {
+        self.path()
+    }
+}
+
+/// Error returned when persisting a temporary file fails.
+#[derive(Debug)]
+pub struct PersistError {
+    /// The underlying IO error.
+    pub error: io::Error,
+    /// The temporary file that couldn't be persisted.
+    pub file: NamedTempFile,
+}
+
+impl From<PersistError> for io::Error {
+    #[inline]
+    fn from(error: PersistError) -> io::Error {
+        error.error
+    }
+}
+
+impl From<PersistError> for NamedTempFile {
+    #[inline]
+    fn from(error: PersistError) -> NamedTempFile {
+        error.file
+    }
+}
+
+impl fmt::Display for PersistError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "failed to persist temporary file: {}", self.error)
+    }
+}
+
+impl error::Error for PersistError {
+    fn description(&self) -> &str {
+        "failed to persist temporary file"
+    }
+    fn cause(&self) -> Option<&error::Error> {
+        Some(&self.error)
+    }
+}
+
+impl NamedTempFile {
+    #[inline]
+    fn inner(&self) -> &NamedTempFileInner {
+        self.0.as_ref().unwrap()
+    }
+
+    #[inline]
+    fn inner_mut(&mut self) -> &mut NamedTempFileInner {
+        self.0.as_mut().unwrap()
+    }
+
+    #[inline]
+    fn take_inner(&mut self) -> NamedTempFileInner {
+        self.0.take().unwrap()
+    }
+
+    /// Create a new named temporary file.
+    ///
+    /// See [`Builder`] for more configuration.
+    ///
+    /// # Security
+    ///
+    /// This will create a temporary file in the default temporary file
+    /// directory (platform dependent). These directories are often patrolled by temporary file
+    /// cleaners so only use this method if you're *positive* that the temporary file cleaner won't
+    /// delete your file.
+    ///
+    /// Reasons to use this method:
+    ///
+    ///   1. The file has a short lifetime and your temporary file cleaner is
+    ///      sane (doesn't delete recently accessed files).
+    ///
+    ///   2. You trust every user on your system (i.e. you are the only user).
+    ///
+    ///   3. You have disabled your system's temporary file cleaner or verified
+    ///      that your system doesn't have a temporary file cleaner.
+    ///
+    /// Reasons not to use this method:
+    ///
+    ///   1. You'll fix it later. No you won't.
+    ///
+    ///   2. You don't care about the security of the temporary file. If none of
+    ///      the "reasons to use this method" apply, referring to a temporary
+    ///      file by name may allow an attacker to create/overwrite your
+    ///      non-temporary files. There are exceptions but if you don't already
+    ///      know them, don't use this method.
+    ///
+    /// # Errors
+    ///
+    /// If the file can not be created, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// Create a named temporary file and write some data to it:
+    ///
+    /// ```no_run
+    /// # use std::io::{self, Write};
+    /// # extern crate tempfile;
+    /// use tempfile::NamedTempFile;
+    ///
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), ::std::io::Error> {
+    /// let mut file = NamedTempFile::new()?;
+    ///
+    /// writeln!(file, "Brian was here. Briefly.")?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// [`Builder`]: struct.Builder.html
+    pub fn new() -> io::Result<NamedTempFile> {
+        Builder::new().tempfile()
+    }
+
+    /// Get the temporary file's path.
+    ///
+    /// # Security
+    ///
+    /// Only use this method if you're positive that a
+    /// temporary file cleaner won't have deleted your file. Otherwise, the path
+    /// returned by this method may refer to an attacker controlled file.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # use std::io::{self, Write};
+    /// # extern crate tempfile;
+    /// use tempfile::NamedTempFile;
+    ///
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), ::std::io::Error> {
+    /// let file = NamedTempFile::new()?;
+    ///
+    /// println!("{:?}", file.path());
+    /// # Ok(())
+    /// # }
+    /// ```
+    #[inline]
+    pub fn path(&self) -> &Path {
+        &self.inner().path
+    }
+
+    /// Close and remove the temporary file.
+    ///
+    /// Use this if you want to detect errors in deleting the file.
+    ///
+    /// # Errors
+    ///
+    /// If the file cannot be deleted, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # extern crate tempfile;
+    /// # use std::io;
+    /// use tempfile::NamedTempFile;
+    ///
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// let file = NamedTempFile::new()?;
+    ///
+    /// // By closing the `NamedTempFile` explicitly, we can check that it has
+    /// // been deleted successfully. If we don't close it explicitly,
+    /// // the file will still be deleted when `file` goes out
+    /// // of scope, but we won't know whether deleting the file
+    /// // succeeded.
+    /// file.close()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn close(mut self) -> io::Result<()> {
+        let NamedTempFileInner { path, file } = self.take_inner();
+        drop(file);
+        fs::remove_file(path)
+    }
+
+    /// Persist the temporary file at the target path.
+    ///
+    /// If a file exists at the target path, persist will atomically replace it.
+    /// If this method fails, it will return `self` in the resulting
+    /// [`PersistError`].
+    ///
+    /// Note: Temporary files cannot be persisted across filesystems.
+    ///
+    /// # Security
+    ///
+    /// Only use this method if you're positive that a
+    /// temporary file cleaner won't have deleted your file. Otherwise, you
+    /// might end up persisting an attacker controlled file.
+    ///
+    /// # Errors
+    ///
+    /// If the file cannot be moved to the new location, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # use std::io::{self, Write};
+    /// # extern crate tempfile;
+    /// use tempfile::NamedTempFile;
+    ///
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// let file = NamedTempFile::new()?;
+    ///
+    /// let mut persisted_file = file.persist("./saved_file.txt")?;
+    /// writeln!(persisted_file, "Brian was here. Briefly.")?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// [`PersistError`]: struct.PersistError.html
+    pub fn persist<P: AsRef<Path>>(mut self, new_path: P) -> Result<File, PersistError> {
+        match imp::persist(&self.inner().path, new_path.as_ref(), true) {
+            Ok(_) => Ok(self.take_inner().file),
+            Err(e) => {
+                Err(PersistError {
+                    file: self,
+                    error: e,
+                })
+            }
+        }
+    }
+
+    /// Persist the temporary file at the target path iff no file exists there.
+    ///
+    /// If a file exists at the target path, fail. If this method fails, it will
+    /// return `self` in the resulting PersistError.
+    ///
+    /// Note: Temporary files cannot be persisted across filesystems. Also Note:
+    /// This method is not atomic. It can leave the original link to the
+    /// temporary file behind.
+    ///
+    /// # Security
+    ///
+    /// Only use this method if you're positive that a
+    /// temporary file cleaner won't have deleted your file. Otherwise, you
+    /// might end up persisting an attacker controlled file.
+    ///
+    /// # Errors
+    ///
+    /// If the file cannot be moved to the new location or a file already exists there,
+    /// `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # use std::io::{self, Write};
+    /// # extern crate tempfile;
+    /// use tempfile::NamedTempFile;
+    ///
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// let file = NamedTempFile::new()?;
+    ///
+    /// let mut persisted_file = file.persist_noclobber("./saved_file.txt")?;
+    /// writeln!(persisted_file, "Brian was here. Briefly.")?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn persist_noclobber<P: AsRef<Path>>(mut self, new_path: P) -> Result<File, PersistError> {
+        match imp::persist(&self.inner().path, new_path.as_ref(), false) {
+            Ok(_) => Ok(self.take_inner().file),
+            Err(e) => {
+                Err(PersistError {
+                    file: self,
+                    error: e,
+                })
+            }
+        }
+    }
+
+    /// Reopen the temporary file.
+    ///
+    /// This function is useful when you need multiple independent handles to
+    /// the same file. It's perfectly fine to drop the original `NamedTempFile`
+    /// while holding on to `File`s returned by this function; the `File`s will
+    /// remain usable. However, they may not be nameable.
+    ///
+    /// # Errors
+    ///
+    /// If the file cannot be reopened, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # use std::io;
+    /// # extern crate tempfile;
+    /// use tempfile::NamedTempFile;
+    ///
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// let file = NamedTempFile::new()?;
+    ///
+    /// let another_handle = file.reopen()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn reopen(&self) -> io::Result<File> {
+        imp::reopen(self.as_file(), NamedTempFile::path(self))
+    }
+
+    /// Get a reference to the underlying file.
+    pub fn as_file(&self) -> &File {
+        &self.inner().file
+    }
+
+    /// Get a mutable reference to the underlying file.
+    pub fn as_file_mut(&mut self) -> &mut File {
+        &mut self.inner_mut().file
+    }
+
+    /// Convert the temporary file into a `std::fs::File`.
+    /// 
+    /// The inner file will be deleted.
+    pub fn into_file(mut self) -> File {
+        let NamedTempFileInner { path, file } = self.take_inner();
+        let _ = fs::remove_file(path);
+        file
+    }
+}
+
+impl Drop for NamedTempFile {
+    fn drop(&mut self) {
+        if let Some(NamedTempFileInner { file, path }) = self.0.take() {
+            drop(file);
+            let _ = fs::remove_file(path);
+        }
+    }
+}
+
+impl Read for NamedTempFile {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        self.as_file_mut().read(buf)
+    }
+}
+
+impl<'a> Read for &'a NamedTempFile {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        self.as_file().read(buf)
+    }
+}
+
+impl Write for NamedTempFile {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.as_file_mut().write(buf)
+    }
+    #[inline]
+    fn flush(&mut self) -> io::Result<()> {
+        self.as_file_mut().flush()
+    }
+}
+
+impl<'a> Write for &'a NamedTempFile {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.as_file().write(buf)
+    }
+    #[inline]
+    fn flush(&mut self) -> io::Result<()> {
+        self.as_file().flush()
+    }
+}
+
+impl Seek for NamedTempFile {
+    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+        self.as_file_mut().seek(pos)
+    }
+}
+
+impl<'a> Seek for &'a NamedTempFile {
+    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+        self.as_file().seek(pos)
+    }
+}
+
+#[cfg(unix)]
+impl std::os::unix::io::AsRawFd for NamedTempFile {
+    #[inline]
+    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
+        self.as_file().as_raw_fd()
+    }
+}
+
+#[cfg(windows)]
+impl std::os::windows::io::AsRawHandle for NamedTempFile {
+    #[inline]
+    fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
+        self.as_file().as_raw_handle()
+    }
+}
+
+// pub(crate)
+pub fn create_named(path: PathBuf) -> io::Result<NamedTempFile> {
+    imp::create_named(&path).map(|file| 
+        NamedTempFile(Some(NamedTempFileInner {
+            path: path,
+            file: file,
+        })))
+}
diff --git a/src/lib.rs b/src/lib.rs
index b12b54d..b6d2f2e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,36 +1,98 @@
-//! Securely create and manage temporary files. Temporary files created by this create are
-//! automatically deleted.
+//! Temporary files and directories.
 //!
-//! This crate provides two temporary file variants: a `tempfile()` function that returns standard
-//! `File` objects and `NamedTempFile`. When choosing between the variants, prefer `tempfile()`
-//! unless you either need to know the file's path or to be able to persist it.
-//!
-//! # Example
-//!
-//! ```
-//! use tempfile::tempfile;
-//! use std::fs::File;
+//! - Use the [`tempfile()`] function for temporary files
+//! - Use the [`tempdir()`] function for temporary directories.
+//! 
+//! # Design
 //!
-//! let mut file: File = tempfile().expect("failed to create temporary file");
-//! ```
+//! This crate provides several approaches to creating temporary files and directories.
+//! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed.
+//! [`TempDir`] and [`NamedTempFile`] both rely on Rust destructors for cleanup.
 //!
-//! # Differences
+//! When choosing between the temporary file variants, prefer `tempfile`
+//! unless you either need to know the file's path or to be able to persist it.
 //!
 //! ## Resource Leaking
 //!
-//! `tempfile()` will (almost) never fail to cleanup temporary files but `NamedTempFile` will if
-//! its destructor doesn't run. This is because `tempfile()` relies on the OS to cleanup the
-//! underlying file so the file while `NamedTempFile` relies on its destructor to do so.
+//! `tempfile` will (almost) never fail to cleanup temporary resources but `TempDir` and `NamedTempFile` will if
+//! their destructors don't run. This is because `tempfile` relies on the OS to cleanup the
+//! underlying file so the file while `TempDir` and `NamedTempFile` rely on their destructors to do so.
 //!
 //! ## Security
 //!
 //! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
 //! a temporary file cleaner could delete the temporary file which an attacker could then replace.
 //!
-//! `tempfile()` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
+//! `tempfile` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
 //! rely on file paths.
 //!
+//! ## Examples
+//!
+//! Create a temporary file and write some data into it:
+//!
+//! ```
+//! # extern crate tempfile;
+//! use tempfile::tempfile;
+//! use std::io::{self, Write};
+//!
+//! # fn main() {
+//! #     if let Err(_) = run() {
+//! #         ::std::process::exit(1);
+//! #     }
+//! # }
+//! # fn run() -> Result<(), io::Error> {
+//! // Create a file inside of `std::env::temp_dir()`.
+//! let mut file = tempfile()?;
+//!
+//! writeln!(file, "Brian was here. Briefly.")?;
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! Create a temporary directory and add a file to it:
+//!
+//! ```
+//! # extern crate tempfile;
+//! use tempfile::tempdir;
+//! use std::fs::File;
+//! use std::io::{self, Write};
+//!
+//! # fn main() {
+//! #     if let Err(_) = run() {
+//! #         ::std::process::exit(1);
+//! #     }
+//! # }
+//! # fn run() -> Result<(), io::Error> {
+//! // Create a directory inside of `std::env::temp_dir()`.
+//! let dir = tempdir()?;
+//!
+//! let file_path = dir.path().join("my-temporary-note.txt");
+//! let mut file = File::create(file_path)?;
+//! writeln!(file, "Brian was here. Briefly.")?;
+//!
+//! // By closing the `TempDir` explicitly, we can check that it has
+//! // been deleted successfully. If we don't close it explicitly,
+//! // the directory will still be deleted when `dir` goes out
+//! // of scope, but we won't know whether deleting the directory
+//! // succeeded.
+//! drop(file);
+//! dir.close()?;
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! [`tempfile()`]: fn.tempfile.html
+//! [`tempdir()`]: fn.tempdir.html
+//! [`TempDir`]: struct.TempDir.html
+//! [`NamedTempFile`]: struct.NamedTempFile.html
+//! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
 
+#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
+       html_root_url = "https://docs.rs/tempfile/2.2.0")]
+#![cfg_attr(test, deny(warnings))]
+
+extern crate remove_dir_all;
 extern crate rand;
 
 #[cfg(unix)]
@@ -45,10 +107,330 @@ extern crate syscall;
 const NUM_RETRIES: u32 = 1 << 31;
 const NUM_RAND_CHARS: usize = 6;
 
+use std::{io, env};
+use std::path::Path;
+
+mod dir;
+mod file;
 mod util;
-mod imp;
-mod named;
-mod unnamed;
 
-pub use named::{NamedTempFile, NamedTempFileOptions, PersistError};
-pub use unnamed::{tempfile, tempfile_in};
+pub use file::{tempfile, tempfile_in, NamedTempFile, PersistError};
+pub use dir::{tempdir, tempdir_in, TempDir};
+
+/// Create a new temporary file or directory with custom parameters.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Builder<'a, 'b> {
+    random_len: usize,
+    prefix: &'a str,
+    suffix: &'b str,
+}
+
+impl<'a, 'b> Builder<'a, 'b> {
+    /// Create a new `Builder`.
+    ///
+    /// # Examples
+    ///
+    /// Create a named temporary file and write some data into it:
+    /// 
+    /// ```
+    /// # extern crate tempfile;
+    /// # use std::io;
+    /// # use std::ffi::OsStr;
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// use tempfile::Builder;
+    ///
+    /// let named_tempfile = Builder::new()
+    ///     .prefix("my-temporary-note")
+    ///     .suffix(".txt")
+    ///     .rand_bytes(5)
+    ///     .tempfile()?;
+    ///
+    /// let name = named_tempfile
+    ///     .path()
+    ///     .file_name().and_then(OsStr::to_str);
+    ///
+    /// if let Some(name) = name {
+    ///     assert!(name.starts_with("my-temporary-note"));
+    ///     assert!(name.ends_with(".txt"));
+    ///     assert_eq!(name.len(), "my-temporary-note.txt".len() + 5);
+    /// }
+    /// # Ok(())
+    /// # }
+    /// ```
+    /// 
+    /// Create a temporary directory and add a file to it:
+    /// 
+    /// ```
+    /// # extern crate tempfile;
+    /// # use std::io::{self, Write};
+    /// # use std::fs::File;
+    /// # use std::ffi::OsStr;
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// use tempfile::Builder;
+    ///
+    /// let dir = Builder::new()
+    ///     .prefix("my-temporary-dir")
+    ///     .rand_bytes(5)
+    ///     .tempdir()?;
+    ///
+    /// let file_path = dir.path().join("my-temporary-note.txt");
+    /// let mut file = File::create(file_path)?;
+    /// writeln!(file, "Brian was here. Briefly.")?;
+    ///
+    /// // By closing the `TempDir` explicitly, we can check that it has
+    /// // been deleted successfully. If we don't close it explicitly,
+    /// // the directory will still be deleted when `dir` goes out
+    /// // of scope, but we won't know whether deleting the directory
+    /// // succeeded.
+    /// drop(file);
+    /// dir.close()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn new() -> Self {
+        Builder {
+            random_len: ::NUM_RAND_CHARS,
+            prefix: ".tmp",
+            suffix: "",
+        }
+    }
+
+    /// Set a custom filename prefix.
+    ///
+    /// Path separators are legal but not advisable.
+    /// Default: `.tmp`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # extern crate tempfile;
+    /// # use std::io;
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// # use tempfile::Builder;
+    /// let named_tempfile = Builder::new()
+    ///     .prefix("my-temporary-note")
+    ///     .tempfile()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn prefix(&mut self, prefix: &'a str) -> &mut Self {
+        self.prefix = prefix;
+        self
+    }
+
+    /// Set a custom filename suffix.
+    ///
+    /// Path separators are legal but not advisable.
+    /// Default: empty.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # extern crate tempfile;
+    /// # use std::io;
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// # use tempfile::Builder;
+    /// let named_tempfile = Builder::new()
+    ///     .suffix(".txt")
+    ///     .tempfile()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn suffix(&mut self, suffix: &'b str) -> &mut Self {
+        self.suffix = suffix;
+        self
+    }
+
+    /// Set the number of random bytes.
+    ///
+    /// Default: `6`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # extern crate tempfile;
+    /// # use std::io;
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// # use tempfile::Builder;
+    /// let named_tempfile = Builder::new()
+    ///     .rand_bytes(5)
+    ///     .tempfile()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
+        self.random_len = rand;
+        self
+    }
+
+    /// Create the named temporary file.
+    ///
+    /// # Security
+    ///
+    /// See [the security][security] docs on `NamedTempFile`.
+    /// 
+    /// # Resource leaking
+    /// 
+    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
+    ///
+    /// # Errors
+    ///
+    /// If the file cannot be created, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # extern crate tempfile;
+    /// # use std::io;
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// # use tempfile::Builder;
+    /// let tempfile = Builder::new().tempfile()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// [security]: struct.NamedTempFile.html#security
+    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
+    pub fn tempfile(&self) -> io::Result<NamedTempFile> {
+        self.tempfile_in(&env::temp_dir())
+    }
+
+    /// Create the named temporary file in the specified directory.
+    ///
+    /// # Security
+    ///
+    /// See [the security][security] docs on `NamedTempFile`.
+    /// 
+    /// # Resource leaking
+    /// 
+    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
+    /// 
+    /// # Errors
+    ///
+    /// If the file cannot be created, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # extern crate tempfile;
+    /// # use std::io;
+    /// # fn main() {
+    /// #     if let Err(_) = run() {
+    /// #         ::std::process::exit(1);
+    /// #     }
+    /// # }
+    /// # fn run() -> Result<(), io::Error> {
+    /// # use tempfile::Builder;
+    /// let tempfile = Builder::new().tempfile_in("./")?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// [security]: struct.NamedTempFile.html#security
+    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
+    pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
+        util::create_helper(dir.as_ref(), self.prefix, self.suffix, self.random_len, file::create_named)
+
+    }
+
+    /// Attempts to make a temporary directory inside of `env::temp_dir()` whose
+    /// name will have the prefix, `prefix`. The directory and
+    /// everything inside it will be automatically deleted once the
+    /// returned `TempDir` is destroyed.
+    ///
+    /// # Resource leaking
+    /// 
+    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
+    /// 
+    /// # Errors
+    ///
+    /// If the directory can not be created, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::fs::File;
+    /// use std::io::Write;
+    /// use tempfile::Builder;
+    ///
+    /// # use std::io;
+    /// # fn run() -> Result<(), io::Error> {
+    /// let tmp_dir = Builder::new().tempdir()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    /// 
+    /// [resource-leaking]: struct.TempDir.html#resource-leaking
+    pub fn tempdir(&self) -> io::Result<TempDir> {
+        self.tempdir_in(&env::temp_dir())
+    }
+
+    /// Attempts to make a temporary directory inside of `dir`.
+    /// The directory and everything inside it will be automatically 
+    /// deleted once the returned `TempDir` is destroyed.
+    /// 
+    /// # Resource leaking
+    /// 
+    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
+    ///
+    /// # Errors
+    ///
+    /// If the directory can not be created, `Err` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::fs::{self, File};
+    /// use std::io::Write;
+    /// use tempfile::Builder;
+    ///
+    /// # use std::io;
+    /// # fn run() -> Result<(), io::Error> {
+    /// let tmp_dir = Builder::new().tempdir_in("./")?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    /// 
+    /// [resource-leaking]: struct.TempDir.html#resource-leaking
+    pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
+        let storage;
+        let mut dir = dir.as_ref();
+        if !dir.is_absolute() {
+            let cur_dir = env::current_dir()?;
+            storage = cur_dir.join(dir);
+            dir = &storage;
+        }
+
+        util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create)
+    }
+}
diff --git a/src/named.rs b/src/named.rs
deleted file mode 100644
index f83db77..0000000
--- a/src/named.rs
+++ /dev/null
@@ -1,393 +0,0 @@
-use std::io::{self, Read, Write, Seek, SeekFrom};
-use std::fs::{self, File};
-use std::path::{Path, PathBuf};
-use std::ops::{Deref, DerefMut};
-use std::error;
-use std::fmt;
-use std::env;
-use std;
-use util;
-
-use super::imp;
-
-/// A named temporary file.
-///
-/// This variant is *NOT* secure/reliable in the presence of a pathological temporary file cleaner.
-///
-/// `NamedTempFiles` are deleted on drop. As rust doesn't guarantee that a struct will ever be
-/// dropped, these temporary files will not be deleted on abort, resource leak, early exit, etc.
-///
-/// Please use `TempFile` unless you absolutely need a named file.
-///
-/// Note: To convert a `NamedTempFile` into a normal temporary file, use the
-/// provided conversion: `let my_file: File = my_temp_file.into();`. The file
-/// will be automatically deleted on close. However, if you do this, the file's
-/// path will no longer be valid.
-pub struct NamedTempFile(Option<NamedTempFileInner>);
-
-impl AsRef<File> for NamedTempFile {
-    fn as_ref(&self) -> &File {
-        self
-    }
-}
-
-impl AsMut<File> for NamedTempFile {
-    fn as_mut(&mut self) -> &mut File {
-        self
-    }
-}
-
-struct NamedTempFileInner {
-    file: File,
-    path: PathBuf,
-}
-
-impl fmt::Debug for NamedTempFile {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "NamedTempFile({:?})", self.0.as_ref().unwrap().path)
-    }
-}
-
-impl Deref for NamedTempFile {
-    type Target = File;
-    #[inline]
-    fn deref(&self) -> &File {
-        &self.inner().file
-    }
-}
-
-impl DerefMut for NamedTempFile {
-    #[inline]
-    fn deref_mut(&mut self) -> &mut File {
-        &mut self.inner_mut().file
-    }
-}
-
-/// Error returned when persisting a temporary file fails
-#[derive(Debug)]
-pub struct PersistError {
-    /// The underlying IO error.
-    pub error: io::Error,
-    /// The temporary file that couldn't be persisted.
-    pub file: NamedTempFile,
-}
-
-impl From<PersistError> for io::Error {
-    #[inline]
-    fn from(error: PersistError) -> io::Error {
-        error.error
-    }
-}
-
-impl From<PersistError> for NamedTempFile {
-    #[inline]
-    fn from(error: PersistError) -> NamedTempFile {
-        error.file
-    }
-}
-
-impl fmt::Display for PersistError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "failed to persist temporary file: {}", self.error)
-    }
-}
-
-impl error::Error for PersistError {
-    fn description(&self) -> &str {
-        "failed to persist temporary file"
-    }
-    fn cause(&self) -> Option<&error::Error> {
-        Some(&self.error)
-    }
-}
-
-impl NamedTempFile {
-    #[inline]
-    fn inner(&self) -> &NamedTempFileInner {
-        self.0.as_ref().unwrap()
-    }
-
-    #[inline]
-    fn inner_mut(&mut self) -> &mut NamedTempFileInner {
-        self.0.as_mut().unwrap()
-    }
-
-    /// Create a new temporary file.
-    ///
-    /// *SECURITY WARNING:* This will create a temporary file in the default temporary file
-    /// directory (platform dependent). These directories are often patrolled by temporary file
-    /// cleaners so only use this method if you're *positive* that the temporary file cleaner won't
-    /// delete your file.
-    ///
-    /// Reasons to use this method:
-    ///
-    ///   1. The file has a short lifetime and your temporary file cleaner is
-    ///      sane (doesn't delete recently accessed files).
-    ///
-    ///   2. You trust every user on your system (i.e. you are the only user).
-    ///
-    ///   3. You have disabled your system's temporary file cleaner or verified
-    ///      that your system doesn't have a temporary file cleaner.
-    ///
-    /// Reasons not to use this method:
-    ///
-    ///   1. You'll fix it later. No you won't.
-    ///
-    ///   2. You don't care about the security of the temporary file. If none of
-    ///      the "reasons to use this method" apply, referring to a temporary
-    ///      file by name may allow an attacker to create/overwrite your
-    ///      non-temporary files. There are exceptions but if you don't already
-    ///      know them, don't use this method.
-    pub fn new() -> io::Result<NamedTempFile> {
-        NamedTempFileOptions::new().create()
-    }
-
-    /// Create a new temporary file in the specified directory.
-    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<NamedTempFile> {
-        NamedTempFileOptions::new().create_in(dir)
-    }
-
-
-    /// Get the temporary file's path.
-    ///
-    /// *SECURITY WARNING:* Only use this method if you're positive that a
-    /// temporary file cleaner won't have deleted your file. Otherwise, the path
-    /// returned by this method may refer to an attacker controlled file.
-    #[inline]
-    pub fn path(&self) -> &Path {
-        &self.inner().path
-    }
-
-    /// Close and remove the temporary file.
-    ///
-    /// Use this if you want to detect errors in deleting the file.
-    pub fn close(mut self) -> io::Result<()> {
-        let NamedTempFileInner { path, file } = self.0.take().unwrap();
-        drop(file);
-        fs::remove_file(path)
-    }
-
-    /// Persist the temporary file at the target path.
-    ///
-    /// If a file exists at the target path, persist will atomically replace it.
-    /// If this method fails, it will return `self` in the resulting
-    /// PersistError.
-    ///
-    /// Note: Temporary files cannot be persisted across filesystems.
-    ///
-    /// *SECURITY WARNING:* Only use this method if you're positive that a
-    /// temporary file cleaner won't have deleted your file. Otherwise, you
-    /// might end up persisting an attacker controlled file.
-    pub fn persist<P: AsRef<Path>>(mut self, new_path: P) -> Result<File, PersistError> {
-        match imp::persist(&self.inner().path, new_path.as_ref(), true) {
-            Ok(_) => Ok(self.0.take().unwrap().file),
-            Err(e) => {
-                Err(PersistError {
-                    file: self,
-                    error: e,
-                })
-            }
-        }
-    }
-
-    /// Persist the temporary file at the target path iff no file exists there.
-    ///
-    /// If a file exists at the target path, fail. If this method fails, it will
-    /// return `self` in the resulting PersistError.
-    ///
-    /// Note: Temporary files cannot be persisted across filesystems. Also Note:
-    /// This method is not atomic. It can leave the original link to the
-    /// temporary file behind.
-    ///
-    /// *SECURITY WARNING:* Only use this method if you're positive that a
-    /// temporary file cleaner won't have deleted your file. Otherwise, you
-    /// might end up persisting an attacker controlled file.
-    pub fn persist_noclobber<P: AsRef<Path>>(mut self, new_path: P) -> Result<File, PersistError> {
-        match imp::persist(&self.inner().path, new_path.as_ref(), false) {
-            Ok(_) => Ok(self.0.take().unwrap().file),
-            Err(e) => {
-                Err(PersistError {
-                    file: self,
-                    error: e,
-                })
-            }
-        }
-    }
-
-    /// Reopen the temporary file.
-    ///
-    /// This function is useful when you need multiple independent handles to
-    /// the same file. It's perfectly fine to drop the original `NamedTempFile`
-    /// while holding on to `File`s returned by this function; the `File`s will
-    /// remain usable. However, they may not be nameable.
-    pub fn reopen(&self) -> io::Result<File> {
-        imp::reopen(self, self.path())
-    }
-}
-
-impl From<NamedTempFile> for File {
-    fn from(mut f: NamedTempFile) -> File {
-        let NamedTempFileInner { path, file } = f.0.take().unwrap();
-        let _ = fs::remove_file(path);
-        file
-    }
-}
-
-impl Drop for NamedTempFile {
-    fn drop(&mut self) {
-        if let Some(NamedTempFileInner { file, path }) = self.0.take() {
-            drop(file);
-            let _ = fs::remove_file(path);
-        }
-    }
-}
-
-impl Read for NamedTempFile {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        (**self).read(buf)
-    }
-}
-
-impl<'a> Read for &'a NamedTempFile {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        (&***self).read(buf)
-    }
-}
-
-impl Write for NamedTempFile {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        (**self).write(buf)
-    }
-    #[inline]
-    fn flush(&mut self) -> io::Result<()> {
-        (**self).flush()
-    }
-}
-
-impl<'a> Write for &'a NamedTempFile {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        (&***self).write(buf)
-    }
-    #[inline]
-    fn flush(&mut self) -> io::Result<()> {
-        (&***self).flush()
-    }
-}
-
-impl Seek for NamedTempFile {
-    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
-        (**self).seek(pos)
-    }
-}
-
-impl<'a> Seek for &'a NamedTempFile {
-    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
-        (&***self).seek(pos)
-    }
-}
-
-#[cfg(unix)]
-impl std::os::unix::io::AsRawFd for NamedTempFile {
-    #[inline]
-    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
-        (**self).as_raw_fd()
-    }
-}
-
-#[cfg(windows)]
-impl std::os::windows::io::AsRawHandle for NamedTempFile {
-    #[inline]
-    fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
-        (**self).as_raw_handle()
-    }
-}
-
-
-/// Create a new temporary file with custom parameters.
-///
-/// # Example
-/// ```
-/// use tempfile::NamedTempFileOptions;
-///
-/// let named_temp_file = NamedTempFileOptions::new()
-///                         .prefix("hogehoge")
-///                         .suffix(".rs")
-///                         .rand_bytes(5)
-///                         .create()
-///                         .unwrap();
-/// let name = named_temp_file.path()
-///   .file_name().unwrap()
-///   .to_str().unwrap();
-///
-/// assert!(name.starts_with("hogehoge"));
-/// assert!(name.ends_with(".rs"));
-/// assert_eq!(name.len(), "hogehoge.rs".len() + 5);
-/// ```
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct NamedTempFileOptions<'a, 'b> {
-    random_len: usize,
-    prefix: &'a str,
-    suffix: &'b str,
-}
-
-impl<'a, 'b> NamedTempFileOptions<'a, 'b> {
-    /// Create a new NamedTempFileOptions
-    pub fn new() -> Self {
-        NamedTempFileOptions {
-            random_len: ::NUM_RAND_CHARS,
-            prefix: ".tmp",
-            suffix: "",
-        }
-    }
-
-    /// Set a custom filename prefix.
-    ///
-    /// Path separators are legal but not advisable.
-    /// Default: ".tmp"
-    pub fn prefix(&mut self, prefix: &'a str) -> &mut Self {
-        self.prefix = prefix;
-        self
-    }
-
-    /// Set a custom filename suffix.
-    ///
-    /// Path separators are legal but not advisable.
-    /// Default: ""
-    pub fn suffix(&mut self, suffix: &'b str) -> &mut Self {
-        self.suffix = suffix;
-        self
-    }
-
-    /// Set the number of random bytes.
-    ///
-    /// Default: 6
-    pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
-        self.random_len = rand;
-        self
-    }
-
-    /// Create the named temporary file.
-    pub fn create(&self) -> io::Result<NamedTempFile> {
-        self.create_in(&env::temp_dir())
-    }
-
-    /// Create the named temporary file in the specified directory.
-    pub fn create_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
-        for _ in 0..::NUM_RETRIES {
-            let path = dir.as_ref().join(util::tmpname(self.prefix, self.suffix, self.random_len));
-            return match imp::create_named(&path) {
-                Ok(file) => {
-                    Ok(NamedTempFile(Some(NamedTempFileInner {
-                        path: path,
-                        file: file,
-                    })))
-                }
-                Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
-                Err(e) => Err(e),
-            };
-        }
-        Err(io::Error::new(io::ErrorKind::AlreadyExists,
-                           "too many temporary files exist"))
-
-    }
-}
diff --git a/src/options.rs b/src/options.rs
deleted file mode 100644
index e69de29..0000000
diff --git a/src/unnamed.rs b/src/unnamed.rs
deleted file mode 100644
index 211f7dc..0000000
--- a/src/unnamed.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use std::fs::File;
-use std::env;
-use std::path::Path;
-use std::io;
-
-use super::imp;
-
-/// Create an unnamed temporary file.
-///
-/// This method is secure/reliable in the presence of a pathological temporary file cleaner.
-///
-/// # Deletion:
-///
-/// ## Linux >= 3.11**
-///
-/// The temporary file is never linked into the filesystem so it can't be leaked.
-///
-/// ## Other *nix
-///
-/// The temporary file is immediately unlinked on create. The OS will delete it
-/// when the last open copy of it is closed.
-///
-/// ## Windows
-///
-/// The temporary file is marked `DeleteOnClose` and, again, will be deleted
-/// when the last open copy of it is closed. Unlike *nix operating systems, the
-/// file is not immediately unlinked from the filesystem.
-pub fn tempfile() -> io::Result<File> {
-    tempfile_in(&env::temp_dir())
-}
-
-/// Create an unnamed temporary file in the specified directory.
-///
-/// See `tempfile()` for more information.
-pub fn tempfile_in<P: AsRef<Path>>(dir: P) -> io::Result<File> {
-    imp::create(dir.as_ref())
-}
diff --git a/src/util.rs b/src/util.rs
index 46c81a1..460f586 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,9 +1,10 @@
 use std::ffi::OsString;
-use std::iter;
+use std::{io, iter};
+use std::path::{Path, PathBuf};
 use rand;
 use rand::Rng;
 
-pub fn tmpname(prefix: &str, suffix: &str, rand_len: usize) -> OsString {
+fn tmpname(prefix: &str, suffix: &str, rand_len: usize) -> OsString {
     let mut buf = String::with_capacity(prefix.len() + suffix.len() + rand_len);
     buf.push_str(prefix);
     buf.extend(iter::repeat('X').take(rand_len));
@@ -25,3 +26,19 @@ pub fn tmpname(prefix: &str, suffix: &str, rand_len: usize) -> OsString {
     }
     OsString::from(buf)
 }
+
+pub fn create_helper<F, R>(base: &Path, prefix: &str, suffix: &str, random_len: usize, f: F) -> io::Result<R>
+where
+    F: Fn(PathBuf) -> io::Result<R>,
+{
+    for _ in 0..::NUM_RETRIES {
+        let path = base.join(tmpname(prefix, suffix, random_len));
+        return match f(path) {
+            Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
+            res => res
+        };
+    }
+
+    Err(io::Error::new(io::ErrorKind::AlreadyExists,
+        "too many temporary files exist"))
+}


Test patch:
diff --git a/tests/namedtempfile.rs b/tests/namedtempfile.rs
index a1ef35f..af6331d 100644
--- a/tests/namedtempfile.rs
+++ b/tests/namedtempfile.rs
@@ -1,5 +1,5 @@
 extern crate tempfile;
-use tempfile::{NamedTempFile, NamedTempFileOptions};
+use tempfile::{NamedTempFile, Builder};
 use std::env;
 use std::io::{Write, Read, Seek, SeekFrom};
 use std::fs::File;
@@ -84,11 +84,11 @@ fn test_persist_noclobber() {
 
 #[test]
 fn test_customnamed() {
-    let tmpfile = NamedTempFileOptions::new()
+    let tmpfile = Builder::new()
         .prefix("tmp")
         .suffix(&".rs".to_string())
         .rand_bytes(12)
-        .create()
+        .tempfile()
         .unwrap();
     let name = tmpfile.path().file_name().unwrap().to_str().unwrap();
     assert!(name.starts_with("tmp"));
@@ -111,13 +111,13 @@ fn test_reopen() {
 }
 
 #[test]
-fn test_to_file() {
+fn test_into_file() {
     let mut file = NamedTempFile::new().unwrap();
     let path = file.path().to_owned();
     write!(file, "abcde").expect("write failed");
 
     assert!(path.exists());
-    let mut file: File = file.into();
+    let mut file = file.into_file();
     assert!(!path.exists());
 
     file.seek(SeekFrom::Start(0)).unwrap();
diff --git a/tests/tempdir.rs b/tests/tempdir.rs
new file mode 100644
index 0000000..181006b
--- /dev/null
+++ b/tests/tempdir.rs
@@ -0,0 +1,243 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate tempfile;
+
+use std::env;
+use std::fs;
+use std::path::Path;
+use std::sync::mpsc::channel;
+use std::thread;
+
+use tempfile::{Builder, TempDir};
+
+macro_rules! t {
+    ($e:expr) => (match $e { Ok(n) => n, Err(e) => panic!("error: {}", e) })
+}
+
+trait PathExt {
+    fn exists(&self) -> bool;
+    fn is_dir(&self) -> bool;
+}
+
+impl PathExt for Path {
+    fn exists(&self) -> bool { fs::metadata(self).is_ok() }
+    fn is_dir(&self) -> bool {
+        fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
+    }
+}
+
+fn test_tempdir() {
+    let path = {
+        let p = t!(Builder::new().prefix("foobar").tempdir_in(&Path::new(".")));
+        let p = p.path();
+        assert!(p.to_str().unwrap().contains("foobar"));
+        p.to_path_buf()
+    };
+    assert!(!path.exists());
+}
+
+#[test]
+fn test_customnamed() {
+    let tmpfile = Builder::new()
+        .prefix("prefix")
+        .suffix("suffix")
+        .rand_bytes(12)
+        .tempdir()
+        .unwrap();
+    let name = tmpfile.path().file_name().unwrap().to_str().unwrap();
+    assert!(name.starts_with("prefix"));
+    assert!(name.ends_with("suffix"));
+    assert_eq!(name.len(), 24);
+}
+
+fn test_rm_tempdir() {
+    let (tx, rx) = channel();
+    let f = move|| -> () {
+        let tmp = t!(TempDir::new());
+        tx.send(tmp.path().to_path_buf()).unwrap();
+        panic!("panic to unwind past `tmp`");
+    };
+    let _ = thread::spawn(f).join();
+    let path = rx.recv().unwrap();
+    assert!(!path.exists());
+
+    let tmp = t!(TempDir::new());
+    let path = tmp.path().to_path_buf();
+    let f = move|| -> () {
+        let _tmp = tmp;
+        panic!("panic to unwind past `tmp`");
+    };
+    let _ = thread::spawn(f).join();
+    assert!(!path.exists());
+
+    let path;
+    {
+        let f = move || {
+            t!(TempDir::new())
+        };
+
+        let tmp = thread::spawn(f).join().unwrap();
+        path = tmp.path().to_path_buf();
+        assert!(path.exists());
+    }
+    assert!(!path.exists());
+
+    let path;
+    {
+        let tmp = t!(TempDir::new());
+        path = tmp.into_path();
+    }
+    assert!(path.exists());
+    t!(fs::remove_dir_all(&path));
+    assert!(!path.exists());
+}
+
+fn test_rm_tempdir_close() {
+    let (tx, rx) = channel();
+    let f = move|| -> () {
+        let tmp = t!(TempDir::new());
+        tx.send(tmp.path().to_path_buf()).unwrap();
+        t!(tmp.close());
+        panic!("panic when unwinding past `tmp`");
+    };
+    let _ = thread::spawn(f).join();
+    let path = rx.recv().unwrap();
+    assert!(!path.exists());
+
+    let tmp = t!(TempDir::new());
+    let path = tmp.path().to_path_buf();
+    let f = move|| -> () {
+        let tmp = tmp;
+        t!(tmp.close());
+        panic!("panic when unwinding past `tmp`");
+    };
+    let _ = thread::spawn(f).join();
+    assert!(!path.exists());
+
+    let path;
+    {
+        let f = move || {
+            t!(TempDir::new())
+        };
+
+        let tmp = thread::spawn(f).join().unwrap();
+        path = tmp.path().to_path_buf();
+        assert!(path.exists());
+        t!(tmp.close());
+    }
+    assert!(!path.exists());
+
+    let path;
+    {
+        let tmp = t!(TempDir::new());
+        path = tmp.into_path();
+    }
+    assert!(path.exists());
+    t!(fs::remove_dir_all(&path));
+    assert!(!path.exists());
+}
+
+// Ideally these would be in std::os but then core would need
+// to depend on std
+fn recursive_mkdir_rel() {
+    let path = Path::new("frob");
+    let cwd = env::current_dir().unwrap();
+    println!("recursive_mkdir_rel: Making: {} in cwd {} [{}]", path.display(),
+           cwd.display(), path.exists());
+    t!(fs::create_dir(&path));
+    assert!(path.is_dir());
+    t!(fs::create_dir_all(&path));
+    assert!(path.is_dir());
+}
+
+fn recursive_mkdir_dot() {
+    let dot = Path::new(".");
+    t!(fs::create_dir_all(&dot));
+    let dotdot = Path::new("..");
+    t!(fs::create_dir_all(&dotdot));
+}
+
+fn recursive_mkdir_rel_2() {
+    let path = Path::new("./frob/baz");
+    let cwd = env::current_dir().unwrap();
+    println!("recursive_mkdir_rel_2: Making: {} in cwd {} [{}]", path.display(),
+             cwd.display(), path.exists());
+    t!(fs::create_dir_all(&path));
+    assert!(path.is_dir());
+    assert!(path.parent().unwrap().is_dir());
+    let path2 = Path::new("quux/blat");
+    println!("recursive_mkdir_rel_2: Making: {} in cwd {}", path2.display(),
+             cwd.display());
+    t!(fs::create_dir("quux"));
+    t!(fs::create_dir_all(&path2));
+    assert!(path2.is_dir());
+    assert!(path2.parent().unwrap().is_dir());
+}
+
+// Ideally this would be in core, but needs TempFile
+pub fn test_remove_dir_all_ok() {
+    let tmpdir = t!(TempDir::new());
+    let tmpdir = tmpdir.path();
+    let root = tmpdir.join("foo");
+
+    println!("making {}", root.display());
+    t!(fs::create_dir(&root));
+    t!(fs::create_dir(&root.join("foo")));
+    t!(fs::create_dir(&root.join("foo").join("bar")));
+    t!(fs::create_dir(&root.join("foo").join("bar").join("blat")));
+    t!(fs::remove_dir_all(&root));
+    assert!(!root.exists());
+    assert!(!root.join("bar").exists());
+    assert!(!root.join("bar").join("blat").exists());
+}
+
+pub fn dont_double_panic() {
+    let r: Result<(), _> = thread::spawn(move|| {
+        let tmpdir = TempDir::new().unwrap();
+        // Remove the temporary directory so that TempDir sees
+        // an error on drop
+        t!(fs::remove_dir(tmpdir.path()));
+        // Panic. If TempDir panics *again* due to the rmdir
+        // error then the process will abort.
+        panic!();
+    }).join();
+    assert!(r.is_err());
+}
+
+fn in_tmpdir<F>(f: F) where F: FnOnce() {
+    let tmpdir = t!(TempDir::new());
+    assert!(env::set_current_dir(tmpdir.path()).is_ok());
+
+    f();
+}
+
+pub fn pass_as_asref_path() {
+    let tempdir = t!(TempDir::new());
+    takes_asref_path(&tempdir);
+
+    fn takes_asref_path<T: AsRef<Path>>(path: T) {
+        let path = path.as_ref();
+        assert!(path.exists());
+    }
+}
+
+#[test]
+fn main() {
+    in_tmpdir(test_tempdir);
+    in_tmpdir(test_rm_tempdir);
+    in_tmpdir(test_rm_tempdir_close);
+    in_tmpdir(recursive_mkdir_rel);
+    in_tmpdir(recursive_mkdir_dot);
+    in_tmpdir(recursive_mkdir_rel_2);
+    in_tmpdir(test_remove_dir_all_ok);
+    in_tmpdir(dont_double_panic);
+    in_tmpdir(pass_as_asref_path);
+}