use std::fmt::Arguments;
use std::io::{self, stdin, stdout, BufRead, BufReader, IoSlice, IoSliceMut, Read, Write};
use std::ops::{Deref, DerefMut};
macro_rules! map_impl {
(
$target:ident,
$($name:ident($($arg:ident: $ty:ty),*)$( -> $ret:ty)?),*
$(,)?
) => {$(
#[inline]
fn $name(&mut self, $($arg: $ty),*) $(-> $ret)? {
self.$target.$name($($arg),*)
}
)*};
}
pub(super) enum Stream<'a, T> {
Owned(T),
Borrowed(&'a mut T),
}
impl<T> Stream<'_, T> {
pub fn retrieve(self) -> T {
if let Self::Owned(t) = self {
t
} else {
unreachable!("the stream must own the type to retrieve it")
}
}
}
impl<T> Deref for Stream<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::Owned(t) => t,
Self::Borrowed(t) => *t as &T,
}
}
}
impl<T> DerefMut for Stream<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::Owned(t) => t,
Self::Borrowed(t) => *t,
}
}
}
pub struct MenuStream<'a, R = super::In, W = super::Out> {
reader: Stream<'a, R>,
writer: Stream<'a, W>,
}
impl Default for MenuStream<'_> {
#[inline]
fn default() -> Self {
Self::wrap_reader(stdin(), stdout())
}
}
impl<R: Read, W> MenuStream<'_, BufReader<R>, W> {
#[inline]
pub fn wrap_reader(reader: R, writer: W) -> Self {
Self::new(BufReader::new(reader), writer)
}
}
impl<'a, R, W> MenuStream<'a, R, W> {
pub fn new(reader: R, writer: W) -> Self {
Self {
reader: Stream::Owned(reader),
writer: Stream::Owned(writer),
}
}
pub fn with(reader: &'a mut R, writer: &'a mut W) -> Self {
Self {
reader: Stream::Borrowed(reader),
writer: Stream::Borrowed(writer),
}
}
#[inline]
pub fn retrieve(self) -> (R, W) {
(self.reader.retrieve(), self.writer.retrieve())
}
}
impl<R: Read, W> Read for MenuStream<'_, R, W> {
map_impl!(
reader,
read(buf: &mut [u8]) -> io::Result<usize>,
read_vectored(bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize>,
read_to_end(buf: &mut Vec<u8>) -> io::Result<usize>,
read_to_string(buf: &mut String) -> io::Result<usize>,
read_exact(buf: &mut [u8]) -> io::Result<()>,
);
}
impl<R: BufRead, W> BufRead for MenuStream<'_, R, W> {
map_impl!(
reader,
fill_buf() -> io::Result<&[u8]>,
consume(amt: usize),
read_until(byte: u8, buf: &mut Vec<u8>) -> io::Result<usize>,
read_line(buf: &mut String) -> io::Result<usize>,
);
}
impl<R, W: Write> Write for MenuStream<'_, R, W> {
map_impl!(
writer,
write(buf: &[u8]) -> io::Result<usize>,
write_vectored(bufs: &[IoSlice<'_>]) -> io::Result<usize>,
flush() -> io::Result<()>,
write_all(buf: &[u8]) -> io::Result<()>,
write_fmt(fmt: Arguments<'_>) -> io::Result<()>,
);
}