#![feature(convert, from_raw_os)]
#![cfg_attr(windows, feature(fs_ext))]
extern crate libc;
extern crate rand;
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;
mod imp;
mod util;
pub struct TempFile(File);
impl TempFile {
#[inline]
pub fn new() -> io::Result<TempFile> {
Self::new_in(&env::temp_dir())
}
#[inline]
pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempFile> {
imp::create(dir.as_ref()).map(|f| TempFile(f))
}
#[inline]
pub fn shared(count: usize) -> io::Result<Vec<TempFile>> {
Self::shared_in(&env::temp_dir(), count)
}
#[inline]
pub fn shared_in<P: AsRef<Path>>(dir: P, count: usize) -> io::Result<Vec<TempFile>> {
imp::create_shared(dir.as_ref(), count).map(|files| {
files.into_iter().map(|f|TempFile(f)).collect()
})
}
#[inline]
pub fn len(&self) -> io::Result<u64> {
self.0.metadata().map(|m| m.len())
}
#[inline(always)]
pub fn set_len(&self, size: u64) -> io::Result<()> {
self.0.set_len(size)
}
#[cfg(any(windows, target_os = "linux"))]
#[inline]
pub fn reopen(&self) -> io::Result<TempFile> {
imp::reopen(&self.0).map(|f|TempFile(f))
}
}
impl Read for TempFile {
#[inline(always)]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl Write for TempFile {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
#[inline(always)]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
impl Seek for TempFile {
#[inline(always)]
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.0.seek(pos)
}
}
#[cfg(unix)]
impl std::os::unix::io::AsRawFd for TempFile {
#[inline(always)]
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
self.0.as_raw_fd()
}
}
#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for TempFile {
#[inline(always)]
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
self.0.as_raw_handle()
}
}
pub struct NamedTempFile(Option<NamedTempFileInner>);
struct NamedTempFileInner {
file: File,
path: PathBuf,
}
impl fmt::Debug for NamedTempFile {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "NamedTempFile({:?})", self.0.as_ref().unwrap().path)
}
}
#[derive(Debug)]
pub struct PersistError {
pub error: io::Error,
pub file: NamedTempFile,
}
impl From<PersistError> for io::Error {
#[inline]
fn from(error: PersistError) -> io::Error {
error.error
}
}
impl fmt::Display for PersistError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "failed to persist temporary file: {}", self.error)
}
}
impl error::Error for PersistError {
#[inline]
fn description(&self) -> &str {
"failed to persist temporary file"
}
#[inline]
fn cause(&self) -> Option<&error::Error> {
Some(&self.error)
}
}
impl NamedTempFile {
#[inline]
pub fn new() -> io::Result<NamedTempFile> {
Self::new_in(&env::temp_dir())
}
#[inline]
pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<NamedTempFile> {
loop {
let path = dir.as_ref().join(&util::tmpname());
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),
}
}
}
#[inline]
pub fn len(&self) -> io::Result<u64> {
self.0.as_ref().unwrap().file.metadata().map(|m| m.len())
}
#[inline]
pub fn set_len(&self, size: u64) -> io::Result<()> {
self.0.as_ref().unwrap().file.set_len(size)
}
#[inline]
pub fn path(&self) -> &Path {
&self.0.as_ref().unwrap().path
}
#[inline]
pub fn close(mut self) -> io::Result<()> {
let NamedTempFileInner { path, file } = self.0.take().unwrap();
drop(file);
fs::remove_file(path)
}
#[inline]
pub fn into_path(mut self) -> PathBuf {
let NamedTempFileInner { path, .. } = self.0.take().unwrap();
path
}
#[inline]
pub fn persist<P: AsRef<Path>>(mut self, new_path: P) -> Result<File, PersistError> {
match fs::rename(&self.0.as_ref().unwrap().path, new_path) {
Ok(_) => Ok(self.0.take().unwrap().file),
Err(e) => Err(PersistError { file: self, error: e }),
}
}
}
impl Drop for NamedTempFile {
#[inline]
fn drop(&mut self) {
if let Some(NamedTempFileInner { file, path }) = self.0.take() {
drop(file);
let _ = fs::remove_file(path);
}
}
}
impl Read for NamedTempFile {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.as_mut().unwrap().file.read(buf)
}
}
impl Write for NamedTempFile {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.as_mut().unwrap().file.write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.0.as_mut().unwrap().file.flush()
}
}
impl Seek for NamedTempFile {
#[inline]
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.0.as_mut().unwrap().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.0.as_ref().unwrap().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.0.as_ref().unwrap().file.as_raw_handle()
}
}