mod mock;
mod streaming;
pub use mock::*;
pub use streaming::*;
use std::fmt::{Display};
use std::str::FromStr;
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("{0}")]
pub struct AccessTerminalError(pub String);
pub trait Terminal {
fn print(&mut self, s: &str) -> Result<(), AccessTerminalError>;
fn print_line(&mut self, s: &str) -> Result<(), AccessTerminalError> {
let mut buf = s.to_owned();
buf.push('\n');
self.print(&buf)
}
fn read_line(&mut self) -> Result<String, AccessTerminalError>;
fn read_from_str_default<V: FromStr + Default>(&mut self, prompt: &str,
) -> Result<V, AccessTerminalError>
where
<V as FromStr>::Err: Display
{
self.read_value(prompt, |str| {
if str.is_empty() {
Ok(V::default())
} else {
str.parse()
}
})
}
fn read_from_str<V: FromStr>(&mut self, prompt: &str,
) -> Result<V, AccessTerminalError>
where
<V as FromStr>::Err: Display
{
self.read_value(prompt, FromStr::from_str)
}
fn read_value<V, E: Display>(
&mut self,
prompt: &str,
parser: impl Fn(&str) -> Result<V, E>,
) -> Result<V, AccessTerminalError> {
loop {
self.print(prompt)?;
let read = self.read_line()?;
let parsed = parser(read.trim());
match parsed {
Ok(val) => return Ok(val),
Err(err) => {
self.print_line(&format!("Invalid input: {err}."))?;
}
}
}
}
}
#[cfg(test)]
mod tests;