1use std::fs::File;
2use std::io;
3use std::io::{BufReader, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write};
4use std::sync::{Arc, Mutex, MutexGuard};
5
6#[derive(Copy, Clone, Debug)]
7pub enum HeapIoError {
8 Closed,
9 NotFile,
10}
11
12#[derive(Clone)]
13pub struct HeapIo {
14 io: Arc<Mutex<Io>>,
15}
16
17impl HeapIo {
18 pub fn from_file(file: File) -> Self {
19 let io = Arc::new(Mutex::new(Io::File(Some(file))));
20 Self { io }
21 }
22
23 pub fn stdin() -> Self {
24 let io = Arc::new(Mutex::new(Io::StdIn));
25 Self { io }
26 }
27
28 pub fn stdout() -> Self {
29 let io = Arc::new(Mutex::new(Io::StdOut));
30 Self { io }
31 }
32
33 pub fn stderr() -> Self {
34 let io = Arc::new(Mutex::new(Io::StdErr));
35 Self { io }
36 }
37
38 pub fn close(&self) {
39 if let Ok(mut guard) = self.io.lock() {
40 *guard = Io::Closed
41 }
42 }
43
44 pub fn to_buf_reader(&self) -> Result<(), HeapIoError> {
45 if let Ok(mut guard) = self.io.lock() {
46 match &mut *guard {
47 Io::File(f) => match f.take() {
48 Some(f) => *guard = Io::FileReadBuf(BufReader::new(f)),
49 None => panic!("file without a file"),
50 },
51 Io::FileReadBuf(_) => return Err(HeapIoError::NotFile),
52 Io::FileWriteBuf(_) => return Err(HeapIoError::NotFile),
53 Io::StdIn => return Err(HeapIoError::NotFile),
54 Io::StdOut => return Err(HeapIoError::NotFile),
55 Io::StdErr => return Err(HeapIoError::NotFile),
56 Io::Closed => return Err(HeapIoError::Closed),
57 }
58 }
59 Ok(())
60 }
61
62 pub fn to_buf_writer(&self) -> Result<(), HeapIoError> {
63 if let Ok(mut guard) = self.io.lock() {
64 match &mut *guard {
65 Io::File(f) => match f.take() {
66 Some(f) => *guard = Io::FileWriteBuf(BufWriter::new(f)),
67 None => panic!("file without a file"),
68 },
69 Io::FileReadBuf(_) => return Err(HeapIoError::NotFile),
70 Io::FileWriteBuf(_) => return Err(HeapIoError::NotFile),
71 Io::StdIn => return Err(HeapIoError::NotFile),
72 Io::StdOut => return Err(HeapIoError::NotFile),
73 Io::StdErr => return Err(HeapIoError::NotFile),
74 Io::Closed => return Err(HeapIoError::Closed),
75 }
76 }
77 Ok(())
78 }
79
80 pub fn get_io(&self) -> IoGuard {
81 let io = self.io.lock().unwrap();
82 IoGuard { io }
83 }
84}
85
86impl Read for HeapIo {
87 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
88 self.io.lock().unwrap().read(buf)
89 }
90}
91
92pub struct IoGuard<'a> {
93 io: MutexGuard<'a, Io>,
94}
95
96impl Read for IoGuard<'_> {
97 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
98 self.io.read(buf)
99 }
100}
101
102impl Write for IoGuard<'_> {
103 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
104 self.io.write(buf)
105 }
106
107 fn flush(&mut self) -> io::Result<()> {
108 self.io.flush()
109 }
110}
111
112impl Seek for IoGuard<'_> {
113 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
114 self.io.seek(pos)
115 }
116}
117
118enum Io {
119 File(Option<File>),
120 FileReadBuf(BufReader<File>),
121 FileWriteBuf(BufWriter<File>),
122 StdIn,
123 StdOut,
124 StdErr,
125 Closed,
126}
127
128impl Read for Io {
129 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
130 match self {
131 Io::File(Some(f)) => f.read(buf),
132 Io::File(None) => panic!("file is missing a file"),
133 Io::FileReadBuf(io) => io.read(buf),
134 Io::FileWriteBuf(_) => Err(io::Error::new(
135 ErrorKind::Unsupported,
136 "read not supported for a write buffer",
137 )),
138 Io::StdIn => io::stdin().read(buf),
139 Io::StdOut => Err(io::Error::new(
140 ErrorKind::Unsupported,
141 "read not supported for stdout",
142 )),
143 Io::StdErr => Err(io::Error::new(
144 ErrorKind::Unsupported,
145 "read not supported for stderr",
146 )),
147 Io::Closed => Err(io::Error::new(
148 ErrorKind::Unsupported,
149 "read not supported for closed",
150 )),
151 }
152 }
153}
154
155impl Write for Io {
156 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
157 match self {
158 Io::File(Some(f)) => f.write(buf),
159 Io::File(None) => panic!("file is missing a file"),
160 Io::FileReadBuf(_) => Err(io::Error::new(
161 ErrorKind::Unsupported,
162 "write not supported for a read buffer",
163 )),
164 Io::FileWriteBuf(io) => io.write(buf),
165 Io::StdIn => Err(io::Error::new(
166 ErrorKind::Unsupported,
167 "write not supported for stdin",
168 )),
169 Io::StdOut => io::stdout().write(buf),
170 Io::StdErr => io::stderr().write(buf),
171 Io::Closed => Err(io::Error::new(
172 ErrorKind::Unsupported,
173 "write not supported for closed",
174 )),
175 }
176 }
177
178 fn flush(&mut self) -> io::Result<()> {
179 match self {
180 Io::File(Some(f)) => f.flush(),
181 Io::File(None) => panic!("file is missing a file"),
182 Io::FileReadBuf(_) => Err(io::Error::new(
183 ErrorKind::Unsupported,
184 "flush not supported for a read buffer",
185 )),
186 Io::FileWriteBuf(io) => io.flush(),
187 Io::StdIn => Err(io::Error::new(
188 ErrorKind::Unsupported,
189 "flush not supported for stdin",
190 )),
191 Io::StdOut => io::stdout().flush(),
192 Io::StdErr => io::stderr().flush(),
193 Io::Closed => Err(io::Error::new(
194 ErrorKind::Unsupported,
195 "flush not supported for closed",
196 )),
197 }
198 }
199}
200
201impl Seek for Io {
202 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
203 match self {
204 Io::File(Some(f)) => f.seek(pos),
205 Io::File(None) => panic!("file is missing a file"),
206 Io::FileReadBuf(io) => io.seek(pos),
207 Io::FileWriteBuf(io) => io.seek(pos),
208 Io::StdIn => Err(io::Error::new(
209 ErrorKind::Unsupported,
210 "seek not supported for stdin",
211 )),
212 Io::StdOut => Err(io::Error::new(
213 ErrorKind::Unsupported,
214 "seek not supported for stdout",
215 )),
216 Io::StdErr => Err(io::Error::new(
217 ErrorKind::Unsupported,
218 "seek not supported for stderr",
219 )),
220 Io::Closed => Err(io::Error::new(
221 ErrorKind::Unsupported,
222 "seek not supported for closed",
223 )),
224 }
225 }
226}