deno/cli/repl.rs
Bartek Iwańczuk 4e1abb4f3a
refactor: use OpError instead of ErrBox for errors in ops (#4058)
To better reflect changes in error types in JS from #3662 this PR changes 
default error type used in ops from "ErrBox" to "OpError".

"OpError" is a type that can be sent over to JSON; it has all 
information needed to construct error in JavaScript. That
made "GetErrorKind" trait useless and so it was removed altogether.

To provide compatibility with previous use of "ErrBox" an implementation of
"From<ErrBox> for OpError" was added, however, it is an escape hatch and
ops implementors should strive to use "OpError" directly.
2020-02-23 14:51:29 -05:00

115 lines
2.7 KiB
Rust

// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::deno_dir::DenoDir;
use crate::op_error::OpError;
use deno_core::ErrBox;
use rustyline;
use std::fs;
use std::path::PathBuf;
#[cfg(not(windows))]
use rustyline::Editor;
// Work around the issue that on Windows, `struct Editor` does not implement the
// `Send` trait, because it embeds a windows HANDLE which is a type alias for
// *mut c_void. This value isn't actually a pointer and there's nothing that
// can be mutated through it, so hack around it. TODO: a prettier solution.
#[cfg(windows)]
use std::ops::{Deref, DerefMut};
#[cfg(windows)]
struct Editor<T: rustyline::Helper> {
inner: rustyline::Editor<T>,
}
#[cfg(windows)]
unsafe impl<T: rustyline::Helper> Send for Editor<T> {}
#[cfg(windows)]
impl<T: rustyline::Helper> Editor<T> {
pub fn new() -> Editor<T> {
Editor {
inner: rustyline::Editor::<T>::new(),
}
}
}
#[cfg(windows)]
impl<T: rustyline::Helper> Deref for Editor<T> {
type Target = rustyline::Editor<T>;
fn deref(&self) -> &rustyline::Editor<T> {
&self.inner
}
}
#[cfg(windows)]
impl<T: rustyline::Helper> DerefMut for Editor<T> {
fn deref_mut(&mut self) -> &mut rustyline::Editor<T> {
&mut self.inner
}
}
pub struct Repl {
editor: Editor<()>,
history_file: PathBuf,
}
impl Repl {
pub fn new(history_file: PathBuf) -> Self {
let mut repl = Self {
editor: Editor::<()>::new(),
history_file,
};
repl.load_history();
repl
}
fn load_history(&mut self) {
debug!("Loading REPL history: {:?}", self.history_file);
self
.editor
.load_history(&self.history_file.to_str().unwrap())
.map_err(|e| {
debug!("Unable to load history file: {:?} {}", self.history_file, e)
})
// ignore this error (e.g. it occurs on first load)
.unwrap_or(())
}
fn save_history(&mut self) -> Result<(), ErrBox> {
fs::create_dir_all(self.history_file.parent().unwrap())?;
self
.editor
.save_history(&self.history_file.to_str().unwrap())
.map(|_| debug!("Saved REPL history to: {:?}", self.history_file))
.map_err(|e| {
eprintln!("Unable to save REPL history: {:?} {}", self.history_file, e);
ErrBox::from(e)
})
}
pub fn readline(&mut self, prompt: &str) -> Result<String, OpError> {
self
.editor
.readline(&prompt)
.map(|line| {
self.editor.add_history_entry(line.clone());
line
})
.map_err(OpError::from)
// Forward error to TS side for processing
}
}
impl Drop for Repl {
fn drop(&mut self) {
self.save_history().unwrap();
}
}
pub fn history_path(dir: &DenoDir, history_file: &str) -> PathBuf {
let mut p: PathBuf = dir.root.clone();
p.push(history_file);
p
}