use std::{io, path::Path};
type OtherError = Box<dyn std::error::Error>;
type OtherResult<T> = Result<T, OtherError>;
pub enum ReadlineResult {
Line(String),
Eof,
Interrupt,
Io(std::io::Error),
Other(OtherError),
}
#[allow(unused)]
mod basic_readline {
use super::*;
pub trait Helper {}
impl<T> Helper for T {}
pub struct Readline<H: Helper> {
helper: H,
}
impl<H: Helper> Readline<H> {
pub fn new(helper: H) -> Self {
Readline { helper }
}
pub fn load_history(&mut self, _path: &Path) -> OtherResult<()> {
Ok(())
}
pub fn save_history(&mut self, _path: &Path) -> OtherResult<()> {
Ok(())
}
pub fn add_history_entry(&mut self, _entry: &str) -> OtherResult<()> {
Ok(())
}
pub fn readline(&mut self, prompt: &str) -> ReadlineResult {
use std::io::prelude::*;
print!("{prompt}");
if let Err(e) = io::stdout().flush() {
return ReadlineResult::Io(e);
}
let next_line = io::stdin().lock().lines().next();
match next_line {
Some(Ok(line)) => ReadlineResult::Line(line),
None => ReadlineResult::Eof,
Some(Err(e)) if e.kind() == io::ErrorKind::Interrupted => ReadlineResult::Interrupt,
Some(Err(e)) => ReadlineResult::Io(e),
}
}
}
}
#[cfg(not(target_arch = "wasm32"))]
mod rustyline_readline {
use super::*;
pub trait Helper: rustyline::Helper {}
impl<T: rustyline::Helper> Helper for T {}
pub struct Readline<H: Helper> {
repl: rustyline::Editor<H, rustyline::history::DefaultHistory>,
}
impl<H: Helper> Readline<H> {
pub fn new(helper: H) -> Self {
use rustyline::*;
let mut repl = Editor::with_config(
Config::builder()
.completion_type(CompletionType::List)
.tab_stop(8)
.bracketed_paste(false) .build(),
)
.expect("failed to initialize line editor");
repl.set_helper(Some(helper));
Readline { repl }
}
pub fn load_history(&mut self, path: &Path) -> OtherResult<()> {
self.repl.load_history(path)?;
Ok(())
}
pub fn save_history(&mut self, path: &Path) -> OtherResult<()> {
if !path.exists() {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
}
self.repl.save_history(path)?;
Ok(())
}
pub fn add_history_entry(&mut self, entry: &str) -> OtherResult<()> {
self.repl.add_history_entry(entry)?;
Ok(())
}
pub fn readline(&mut self, prompt: &str) -> ReadlineResult {
use rustyline::error::ReadlineError;
loop {
break match self.repl.readline(prompt) {
Ok(line) => ReadlineResult::Line(line),
Err(ReadlineError::Interrupted) => ReadlineResult::Interrupt,
Err(ReadlineError::Eof) => ReadlineResult::Eof,
Err(ReadlineError::Io(e)) => ReadlineResult::Io(e),
Err(ReadlineError::WindowResized) => continue,
Err(e) => ReadlineResult::Other(e.into()),
};
}
}
}
}
#[cfg(target_arch = "wasm32")]
use basic_readline as readline_inner;
#[cfg(not(target_arch = "wasm32"))]
use rustyline_readline as readline_inner;
pub use readline_inner::Helper;
pub struct Readline<H: Helper>(readline_inner::Readline<H>);
impl<H: Helper> Readline<H> {
pub fn new(helper: H) -> Self {
Readline(readline_inner::Readline::new(helper))
}
pub fn load_history(&mut self, path: &Path) -> OtherResult<()> {
self.0.load_history(path)
}
pub fn save_history(&mut self, path: &Path) -> OtherResult<()> {
self.0.save_history(path)
}
pub fn add_history_entry(&mut self, entry: &str) -> OtherResult<()> {
self.0.add_history_entry(entry)
}
pub fn readline(&mut self, prompt: &str) -> ReadlineResult {
self.0.readline(prompt)
}
}