| use crate::file::tempfile; | 
 | use std::fs::File; | 
 | use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; | 
 |  | 
 | /// A wrapper for the two states of a `SpooledTempFile`. | 
 | #[derive(Debug)] | 
 | pub enum SpooledData { | 
 |     InMemory(Cursor<Vec<u8>>), | 
 |     OnDisk(File), | 
 | } | 
 |  | 
 | /// An object that behaves like a regular temporary file, but keeps data in | 
 | /// memory until it reaches a configured size, at which point the data is | 
 | /// written to a temporary file on disk, and further operations use the file | 
 | /// on disk. | 
 | #[derive(Debug)] | 
 | pub struct SpooledTempFile { | 
 |     max_size: usize, | 
 |     inner: SpooledData, | 
 | } | 
 |  | 
 | /// Create a new spooled temporary file. | 
 | /// | 
 | /// # 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. | 
 | /// | 
 | /// # Examples | 
 | /// | 
 | /// ``` | 
 | /// use tempfile::spooled_tempfile; | 
 | /// use std::io::{self, Write}; | 
 | /// | 
 | /// # fn main() { | 
 | /// #     if let Err(_) = run() { | 
 | /// #         ::std::process::exit(1); | 
 | /// #     } | 
 | /// # } | 
 | /// # fn run() -> Result<(), io::Error> { | 
 | /// let mut file = spooled_tempfile(15); | 
 | /// | 
 | /// writeln!(file, "short line")?; | 
 | /// assert!(!file.is_rolled()); | 
 | /// | 
 | /// // as a result of this write call, the size of the data will exceed | 
 | /// // `max_size` (15), so it will be written to a temporary file on disk, | 
 | /// // and the in-memory buffer will be dropped | 
 | /// writeln!(file, "marvin gardens")?; | 
 | /// assert!(file.is_rolled()); | 
 | /// | 
 | /// # Ok(()) | 
 | /// # } | 
 | /// ``` | 
 | #[inline] | 
 | pub fn spooled_tempfile(max_size: usize) -> SpooledTempFile { | 
 |     SpooledTempFile::new(max_size) | 
 | } | 
 |  | 
 | impl SpooledTempFile { | 
 |     pub fn new(max_size: usize) -> SpooledTempFile { | 
 |         SpooledTempFile { | 
 |             max_size: max_size, | 
 |             inner: SpooledData::InMemory(Cursor::new(Vec::new())), | 
 |         } | 
 |     } | 
 |  | 
 |     /// Returns true if the file has been rolled over to disk. | 
 |     pub fn is_rolled(&self) -> bool { | 
 |         match self.inner { | 
 |             SpooledData::InMemory(_) => false, | 
 |             SpooledData::OnDisk(_) => true, | 
 |         } | 
 |     } | 
 |  | 
 |     /// Rolls over to a file on disk, regardless of current size. Does nothing | 
 |     /// if already rolled over. | 
 |     pub fn roll(&mut self) -> io::Result<()> { | 
 |         if !self.is_rolled() { | 
 |             let mut file = tempfile()?; | 
 |             if let SpooledData::InMemory(ref mut cursor) = self.inner { | 
 |                 file.write_all(cursor.get_ref())?; | 
 |                 file.seek(SeekFrom::Start(cursor.position()))?; | 
 |             } | 
 |             self.inner = SpooledData::OnDisk(file); | 
 |         } | 
 |         Ok(()) | 
 |     } | 
 |  | 
 |     pub fn set_len(&mut self, size: u64) -> Result<(), io::Error> { | 
 |         if size as usize > self.max_size { | 
 |             self.roll()?; // does nothing if already rolled over | 
 |         } | 
 |         match self.inner { | 
 |             SpooledData::InMemory(ref mut cursor) => { | 
 |                 cursor.get_mut().resize(size as usize, 0); | 
 |                 Ok(()) | 
 |             } | 
 |             SpooledData::OnDisk(ref mut file) => file.set_len(size), | 
 |         } | 
 |     } | 
 |  | 
 |     /// Consumes and returns the inner `SpooledData` type. | 
 |     pub fn into_inner(self) -> SpooledData { | 
 |         self.inner | 
 |     } | 
 | } | 
 |  | 
 | impl Read for SpooledTempFile { | 
 |     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | 
 |         match self.inner { | 
 |             SpooledData::InMemory(ref mut cursor) => cursor.read(buf), | 
 |             SpooledData::OnDisk(ref mut file) => file.read(buf), | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | impl Write for SpooledTempFile { | 
 |     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | 
 |         // roll over to file if necessary | 
 |         let mut rolling = false; | 
 |         if let SpooledData::InMemory(ref mut cursor) = self.inner { | 
 |             rolling = cursor.position() as usize + buf.len() > self.max_size; | 
 |         } | 
 |         if rolling { | 
 |             self.roll()?; | 
 |         } | 
 |  | 
 |         // write the bytes | 
 |         match self.inner { | 
 |             SpooledData::InMemory(ref mut cursor) => cursor.write(buf), | 
 |             SpooledData::OnDisk(ref mut file) => file.write(buf), | 
 |         } | 
 |     } | 
 |  | 
 |     #[inline] | 
 |     fn flush(&mut self) -> io::Result<()> { | 
 |         match self.inner { | 
 |             SpooledData::InMemory(ref mut cursor) => cursor.flush(), | 
 |             SpooledData::OnDisk(ref mut file) => file.flush(), | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | impl Seek for SpooledTempFile { | 
 |     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { | 
 |         match self.inner { | 
 |             SpooledData::InMemory(ref mut cursor) => cursor.seek(pos), | 
 |             SpooledData::OnDisk(ref mut file) => file.seek(pos), | 
 |         } | 
 |     } | 
 | } |