use crate::terminal::{AccessTerminalError, Terminal};
use std::io;
use std::io::{stdin, stdout, Write};
pub struct Streaming<I: Input, O: Output> {
pub input: I,
pub output: O,
}
impl<'a> Default for Streaming<InputAdapter<'a>, OutputAdapter<'a>> {
fn default() -> Self {
Self {
input: InputAdapter::default(),
output: OutputAdapter::default(),
}
}
}
impl<I: Input, O: Output> Terminal for Streaming<I, O> {
fn print(&mut self, s: &str) -> Result<(), AccessTerminalError> {
self.output.print(s)
}
fn read_line(&mut self) -> Result<String, AccessTerminalError> {
self.input.read_line()
}
}
pub trait Input {
fn read_line(&mut self) -> Result<String, AccessTerminalError>;
}
pub type InputReader<'a> = Box<dyn FnMut() -> Result<String, AccessTerminalError> + 'a>;
pub struct InputAdapter<'a>(pub InputReader<'a>);
impl<'a> InputAdapter<'a> {
pub fn new<F>(f: F) -> Self
where
F: FnMut() -> Result<String, AccessTerminalError> + 'a,
{
Self(Box::new(f))
}
}
impl Default for InputAdapter<'_> {
fn default() -> Self {
Self(Box::new(|| {
let mut buf = String::default();
stdin().read_line(&mut buf)?;
Ok(buf)
})) }
}
impl Input for InputAdapter<'_> {
fn read_line(&mut self) -> Result<String, AccessTerminalError> {
self.0()
}
}
pub trait Output {
fn print(&mut self, s: &str) -> Result<(), AccessTerminalError>;
}
pub type OutputWriter<'a> = Box<dyn FnMut(&str) -> Result<(), AccessTerminalError> + 'a>;
pub struct OutputAdapter<'a>(pub OutputWriter<'a>);
impl<'a> OutputAdapter<'a> {
pub fn new<F>(f: F) -> Self
where
F: FnMut(&str) -> Result<(), AccessTerminalError> + 'a,
{
Self(Box::new(f))
}
}
impl Default for OutputAdapter<'_> {
fn default() -> Self {
Self(Box::new(|str| {
print!("{str}");
stdout().flush()?;
Ok(())
}))
}
}
impl Output for OutputAdapter<'_> {
fn print(&mut self, s: &str) -> Result<(), AccessTerminalError> {
self.0(s)
}
}
impl From<io::Error> for AccessTerminalError {
fn from(err: io::Error) -> Self {
Self(err.to_string())
}
}
#[cfg(test)]
mod tests;