use std::io::{BufRead, Read, Result, Write};
use crate::wrapper::{Wrapper, WrapperOwned};
pub trait LineReadExt: BufRead
{
fn line_reader(self) -> LineReader<Self, ()>
where
Self: Sized,
{
LineReader::<_, ()>::new(self)
}
fn line_reader_with<F: FnMut()>(self, pre_read: F) -> LineReader<Self, F>
where
Self: Sized,
{
LineReader::<_, F>::new_with(self, pre_read)
}
}
pub trait PromptWriteExt: Write
{
fn prompt_writer<P: AsRef<[u8]>>(self, prompt: P) -> PromptWriter<Self, P>
where
Self: Sized,
{
PromptWriter::<_, P>::new(self, prompt)
}
fn prompt_writer_with<P: AsRef<[u8]>, F: FnMut() -> P>(
self, prompt: F,
) -> PromptFnWriter<Self, F>
where
Self: Sized,
{
PromptFnWriter::<_, F>::new(self, prompt)
}
}
pub trait LineRead
{
fn read_line(&mut self, buffer: &mut String) -> Result<()>;
fn read_line_with_prompt<W: PromptWrite>(
&mut self, buffer: &mut String, prompt_writer: &mut W,
) -> Result<()>;
}
pub trait PromptWrite
{
fn write_prompt(&mut self) -> Result<()>;
fn flush(&mut self) -> Result<()>;
}
#[derive(Debug, Eq, PartialEq)]
pub struct PromptWriter<W: ?Sized, P: AsRef<[u8]>>
{
prompt: P,
inner: W,
}
#[derive(Debug, Eq, PartialEq)]
pub struct PromptFnWriter<W: ?Sized, P>
{
prompt_fn: P,
inner: W,
}
#[derive(Debug, Eq, PartialEq)]
pub struct LineReader<R: ?Sized, P>
{
pre_read: P,
inner: R,
}
impl<R: BufRead> LineReadExt for R {}
impl<W: Write> PromptWriteExt for W {}
impl<W: ?Sized, P: AsRef<[u8]>, F: FnMut() -> P> PromptFnWriter<W, F>
{
pub fn new(inner: W, prompt_fn: F) -> Self
where
W: Sized,
{
Self { inner, prompt_fn }
}
}
impl<W: ?Sized, P: AsRef<[u8]>> PromptWriter<W, P>
{
pub fn new(inner: W, prompt: P) -> Self
where
W: Sized,
{
Self { inner, prompt }
}
}
impl<R: ?Sized> LineReader<R, ()>
{
pub fn new(inner: R) -> Self
where
R: Sized,
{
Self { inner, pre_read: () }
}
}
impl<R: ?Sized, P: FnMut()> LineReader<R, P>
{
pub fn new_with(inner: R, pre_read: P) -> Self
where
R: Sized,
{
Self { inner, pre_read }
}
}
impl<W: ?Sized, P: AsRef<[u8]>, F: FnMut() -> P> Wrapper<W>
for PromptFnWriter<W, F>
{
fn inner(&self) -> &W
{
&self.inner
}
fn inner_mut(&mut self) -> &mut W
{
&mut self.inner
}
}
impl<W, P: AsRef<[u8]>, F: FnMut() -> P> WrapperOwned<W>
for PromptFnWriter<W, F>
{
fn into_inner(self) -> W
{
self.inner
}
}
impl<W: ?Sized, P: AsRef<[u8]>> Wrapper<W> for PromptWriter<W, P>
{
fn inner(&self) -> &W
{
&self.inner
}
fn inner_mut(&mut self) -> &mut W
{
&mut self.inner
}
}
impl<W, P: AsRef<[u8]>> WrapperOwned<W> for PromptWriter<W, P>
{
fn into_inner(self) -> W
{
self.inner
}
}
impl<R: ?Sized, P> Wrapper<R> for LineReader<R, P>
{
fn inner(&self) -> &R
{
&self.inner
}
fn inner_mut(&mut self) -> &mut R
{
&mut self.inner
}
}
impl<R, P> WrapperOwned<R> for LineReader<R, P>
{
fn into_inner(self) -> R
where
R: Sized,
{
self.inner
}
}
impl<W: Write, P: AsRef<[u8]>> PromptWrite for PromptWriter<W, P>
{
fn write_prompt(&mut self) -> Result<()>
{
self.inner.write_all(self.prompt.as_ref())
}
fn flush(&mut self) -> Result<()>
{
self.inner.flush()
}
}
impl<W: Write, P: AsRef<[u8]>, F: FnMut() -> P> PromptWrite
for PromptFnWriter<W, F>
{
fn write_prompt(&mut self) -> Result<()>
{
self.inner.write_all((self.prompt_fn)().as_ref())
}
fn flush(&mut self) -> Result<()>
{
self.inner.flush()
}
}
impl<R: BufRead + ?Sized> LineRead for LineReader<R, ()>
{
fn read_line(&mut self, buffer: &mut String) -> Result<()>
{
self.inner.read_line(buffer)?;
Ok(())
}
fn read_line_with_prompt<W: PromptWrite>(
&mut self, buffer: &mut String, prompt_writer: &mut W,
) -> Result<()>
{
prompt_writer.write_prompt()?;
prompt_writer.flush()?;
self.read_line(buffer)
}
}
impl<R: BufRead + ?Sized, P: FnMut()> LineRead for LineReader<R, P>
{
fn read_line(&mut self, buffer: &mut String) -> Result<()>
{
(self.pre_read)();
self.inner.read_line(buffer)?;
Ok(())
}
fn read_line_with_prompt<W: PromptWrite>(
&mut self, buffer: &mut String, prompt_writer: &mut W,
) -> Result<()>
{
(self.pre_read)();
prompt_writer.write_prompt()?;
prompt_writer.flush()?;
self.read_line(buffer)
}
}
impl<R: Read + Clone, P: Fn() + Clone> Clone for LineReader<R, P>
{
fn clone(&self) -> Self
{
Self {
inner: self.inner.clone(),
pre_read: self.pre_read.clone(),
}
}
}
#[cfg(test)]
mod tests
{
use std::io::{pipe, stdout, BufReader, PipeReader};
use super::*;
const INPUT_LIST: [&str; 3] = ["first", "second", "third"];
const TEST_OUTPUT_STR: &str = "test Prompt Writer\n";
const TEST_OUTPUT_NUM: u32 = 10000;
fn input() -> String
{
INPUT_LIST.join("\n")
}
fn check_read_line(mut read_fn: impl FnMut(&mut String))
{
let mut buffer = String::new();
let mut iter = INPUT_LIST.into_iter().peekable();
while let Some(i) = iter.next()
{
buffer.clear();
read_fn(&mut buffer);
let right =
iter.peek().map_or_else(|| i.to_string(), |_| format!("{i}\n"));
assert_eq!(buffer, right);
}
}
fn check_prompt_writer(
mut pipe_reader: PipeReader,
mut sync_write_fn: impl FnMut(&mut String) + Send + 'static,
)
{
let handle = std::thread::spawn(move || {
let mut buffer = String::new();
sync_write_fn(&mut buffer);
});
let mut read = String::new();
pipe_reader.read_to_string(&mut read).unwrap();
assert_eq!(read, TEST_OUTPUT_STR);
handle.join().unwrap();
}
fn check_prompt_writer_counter(
pipe_reader: PipeReader,
mut sync_write_fn: impl FnMut(&mut String) + Send + 'static,
)
{
let handle = std::thread::spawn(move || {
let mut buffer = String::new();
for _ in 0..TEST_OUTPUT_NUM
{
buffer.clear();
sync_write_fn(&mut buffer);
}
});
let mut pipe_reader = BufReader::new(pipe_reader);
let mut read = String::new();
for i in 0..TEST_OUTPUT_NUM
{
read.clear();
pipe_reader.read_line(&mut read).unwrap();
assert_eq!(read, format!("{i}\n"));
}
handle.join().unwrap();
}
#[test]
fn test_line_reader()
{
let input = input();
let mut reader = input.as_bytes().line_reader();
check_read_line(move |buffer| reader.read_line(buffer).unwrap());
}
#[test]
fn test_line_reader_with_prompt_writer()
{
let input = input();
let mut reader = input.as_bytes().line_reader();
let mut writer = stdout().prompt_writer(TEST_OUTPUT_STR);
check_read_line(move |buffer| {
reader.read_line_with_prompt(buffer, &mut writer).unwrap()
});
}
#[test]
fn test_prompt_writer()
{
let (pipe_reader, writer) = pipe().unwrap();
let mut writer = writer.prompt_writer(TEST_OUTPUT_STR);
check_prompt_writer(pipe_reader, move |_buffer| {
writer.write_prompt().unwrap();
writer.flush().unwrap();
});
}
#[test]
fn test_prompt_writer_counter()
{
let (pipe_reader, writer) = pipe().unwrap();
let mut counter = -1;
let mut writer = writer.prompt_writer_with(move || {
counter += 1;
format!("{counter}\n")
});
check_prompt_writer_counter(pipe_reader, move |_buffer| {
writer.write_prompt().unwrap();
writer.flush().unwrap();
});
}
#[test]
fn test_prompt_writer_with_line_reader()
{
let (pipe_reader, writer) = pipe().unwrap();
let mut writer = writer.prompt_writer(TEST_OUTPUT_STR);
check_prompt_writer(pipe_reader, move |buffer| {
let input = input();
let mut reader = input.as_bytes().line_reader();
reader.read_line_with_prompt(buffer, &mut writer).unwrap()
});
}
#[test]
fn test_prompt_writer_counter_with_line_reader()
{
let (pipe_reader, writer) = pipe().unwrap();
let mut counter = -1;
let mut writer = writer.prompt_writer_with(move || {
counter += 1;
format!("{counter}\n")
});
check_prompt_writer_counter(pipe_reader, move |buffer| {
let input = input();
let mut reader = input.as_bytes().line_reader();
reader.read_line_with_prompt(buffer, &mut writer).unwrap()
});
}
}