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}