refactor: move REPL logic to separate module

This commit is contained in:
Erica Marigold 2023-08-07 12:28:55 +05:30
parent 09050be633
commit 87cca0f04b
No known key found for this signature in database
GPG key ID: 23CD97ABBBCC5ED2
3 changed files with 117 additions and 89 deletions

View file

@ -53,6 +53,18 @@ opt-level = "z"
strip = true
lto = true
[profile.dev]
lto = "off"
strip = "none"
codegen-units = 256
panic = "unwind"
opt-level = 0
incremental = true
overflow-checks = true
debug-assertions = true
debug = true
rpath = false
# All of the dependencies for Lune.
#
# Dependencies are categorized as following:

View file

@ -18,6 +18,7 @@ use tokio::{
};
pub(crate) mod gen;
pub(crate) mod repl;
pub(crate) mod setup;
pub(crate) mod utils;
@ -154,100 +155,14 @@ impl Cli {
if generate_file_requested {
return Ok(ExitCode::SUCCESS);
}
// HACK: We know that we didn't get any arguments here but since
// script_path is optional clap will not error on its own, to fix
// we will duplicate the CLI command and fetch the version of
// lune to display
let cmd = Cli::command();
let lune_version = cmd.get_version();
let exit_code_status = repl::show_interface(Cli::command()).await;
// The version is mandatory and will always exist
println!("Lune v{}", lune_version.unwrap());
let lune_instance = Lune::new();
let mut repl = DefaultEditor::new()?;
match repl.load_history(&(|| -> PathBuf {
let dir_opt = home::home_dir();
if let Some(dir) = dir_opt {
dir.join(".lune_history")
} else {
eprintln!("Failed to find user home directory, abort!");
// Doesn't feel right to exit directly with a exit code of 1
// Lmk if there is a better way of doing this
exit(1);
}
})()) {
Ok(_) => (),
Err(err) => {
match err {
err => {
if let ReadlineError::Io(io_err) = err {
if io_err.kind() == ErrorKind::NotFound {
std::fs::write(
// We know for sure that the home dir already exists
home::home_dir().unwrap().join(".lune_history"),
String::new(),
)?;
}
}
}
}
eprintln!("WARN: Failed to load REPL history")
}
};
// repl.bind_sequence(
// KeyEvent(KeyCode::Enter, Modifiers::SHIFT),
// EventHandler::Simple(Cmd::AcceptOrInsertLine { accept_in_the_middle: true }),
// );
let mut interrupt_counter = 0u32;
loop {
let mut source_code = String::new();
match repl.readline("> ") {
Ok(code) => {
source_code = code.clone();
repl.add_history_entry(code.as_str())?;
// If source code eval was requested, we reset the counter
interrupt_counter = 0;
}
Err(ReadlineError::Interrupted) => {
// HACK: We actually want the user to do ^C twice to exit,
// but the user would need to ^C one more time even after
// the check passes, so we check for 1 instead of 2
if interrupt_counter != 1 {
println!("Interrupt: ^C again to exit");
// Increment the counter
interrupt_counter += 1;
} else {
break;
}
}
Err(ReadlineError::Eof) => break,
Err(err) => {
eprintln!("REPL ERROR: {}", err.to_string());
break;
}
};
let eval_result = lune_instance.run("REPL", source_code).await;
match eval_result {
Ok(_) => (),
Err(err) => eprintln!("{}", err),
}
}
return Ok(ExitCode::SUCCESS);
return exit_code_status;
}
// Figure out if we should read from stdin or from a file,
// reading from stdin is marked by passing a single "-"

101
src/cli/repl.rs Normal file
View file

@ -0,0 +1,101 @@
use std::{
io::{Error, ErrorKind},
path::PathBuf,
process::{exit, ExitCode},
};
use clap::Command;
use lune::{Lune, LuneError};
use rustyline::{error::ReadlineError, DefaultEditor};
use super::Cli;
// Isn't dependency injection plain awesome?!
pub async fn show_interface(cmd: Command) -> Result<ExitCode, anyhow::Error> {
let lune_version = cmd.get_version();
// The version is mandatory and will always exist
println!("Lune v{}", lune_version.unwrap());
let lune_instance = Lune::new();
let mut repl = DefaultEditor::new()?;
match repl.load_history(&(|| -> PathBuf {
let dir_opt = home::home_dir();
if let Some(dir) = dir_opt {
dir.join(".lune_history")
} else {
eprintln!("Failed to find user home directory, abort!");
// Doesn't feel right to exit directly with a exit code of 1
// Lmk if there is a better way of doing this
exit(1);
}
})()) {
Ok(_) => (),
Err(err) => {
match err {
err => {
if let ReadlineError::Io(io_err) = err {
if io_err.kind() == ErrorKind::NotFound {
std::fs::write(
// We know for sure that the home dir already exists
home::home_dir().unwrap().join(".lune_history"),
String::new(),
)?;
}
}
}
}
eprintln!("WARN: Failed to load REPL history")
}
};
let mut interrupt_counter = 0u32;
loop {
let mut source_code = String::new();
match repl.readline("> ") {
Ok(code) => {
source_code = code.clone();
repl.add_history_entry(code.as_str())?;
// If source code eval was requested, we reset the counter
interrupt_counter = 0;
}
Err(ReadlineError::Interrupted) => {
// HACK: We actually want the user to do ^C twice to exit,
// but the user would need to ^C one more time even after
// the check passes, so we check for 1 instead of 2
if interrupt_counter != 1 {
println!("Interrupt: ^C again to exit");
// Increment the counter
interrupt_counter += 1;
} else {
break;
}
}
Err(ReadlineError::Eof) => break,
Err(err) => {
eprintln!("REPL ERROR: {}", err.to_string());
// This isn't a good way to exit imo, once again
exit(1);
}
};
let eval_result = lune_instance.run("REPL", source_code).await;
match eval_result {
Ok(_) => (),
Err(err) => eprintln!("{}", err),
};
}
Ok(ExitCode::SUCCESS)
}