1use crate::file::tempfile;
2use std::fs::File;
3use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};
4
5#[derive(Debug)]
7pub enum SpooledData {
8 InMemory(Cursor<Vec<u8>>),
9 OnDisk(File),
10}
11
12#[derive(Debug)]
17pub struct SpooledTempFile {
18 max_size: usize,
19 inner: SpooledData,
20}
21
22#[inline]
62pub fn spooled_tempfile(max_size: usize) -> SpooledTempFile {
63 SpooledTempFile::new(max_size)
64}
65
66impl SpooledTempFile {
67 pub fn new(max_size: usize) -> SpooledTempFile {
68 SpooledTempFile {
69 max_size: max_size,
70 inner: SpooledData::InMemory(Cursor::new(Vec::new())),
71 }
72 }
73
74 pub fn is_rolled(&self) -> bool {
76 match self.inner {
77 SpooledData::InMemory(_) => false,
78 SpooledData::OnDisk(_) => true,
79 }
80 }
81
82 pub fn roll(&mut self) -> io::Result<()> {
85 if !self.is_rolled() {
86 let mut file = tempfile()?;
87 if let SpooledData::InMemory(ref mut cursor) = self.inner {
88 file.write_all(cursor.get_ref())?;
89 file.seek(SeekFrom::Start(cursor.position()))?;
90 }
91 self.inner = SpooledData::OnDisk(file);
92 }
93 Ok(())
94 }
95
96 pub fn set_len(&mut self, size: u64) -> Result<(), io::Error> {
97 if size as usize > self.max_size {
98 self.roll()?; }
100 match self.inner {
101 SpooledData::InMemory(ref mut cursor) => {
102 cursor.get_mut().resize(size as usize, 0);
103 Ok(())
104 }
105 SpooledData::OnDisk(ref mut file) => file.set_len(size),
106 }
107 }
108
109 pub fn into_inner(self) -> SpooledData {
111 self.inner
112 }
113}
114
115impl Read for SpooledTempFile {
116 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
117 match self.inner {
118 SpooledData::InMemory(ref mut cursor) => cursor.read(buf),
119 SpooledData::OnDisk(ref mut file) => file.read(buf),
120 }
121 }
122}
123
124impl Write for SpooledTempFile {
125 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
126 let mut rolling = false;
128 if let SpooledData::InMemory(ref mut cursor) = self.inner {
129 rolling = cursor.position() as usize + buf.len() > self.max_size;
130 }
131 if rolling {
132 self.roll()?;
133 }
134
135 match self.inner {
137 SpooledData::InMemory(ref mut cursor) => cursor.write(buf),
138 SpooledData::OnDisk(ref mut file) => file.write(buf),
139 }
140 }
141
142 #[inline]
143 fn flush(&mut self) -> io::Result<()> {
144 match self.inner {
145 SpooledData::InMemory(ref mut cursor) => cursor.flush(),
146 SpooledData::OnDisk(ref mut file) => file.flush(),
147 }
148 }
149}
150
151impl Seek for SpooledTempFile {
152 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
153 match self.inner {
154 SpooledData::InMemory(ref mut cursor) => cursor.seek(pos),
155 SpooledData::OnDisk(ref mut file) => file.seek(pos),
156 }
157 }
158}