-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: extract InteractiveShell as trait + refactor
- Loading branch information
Showing
16 changed files
with
758 additions
and
528 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,112 +1,57 @@ | ||
use std::io::Write; | ||
|
||
use crate::{ | ||
interactive_shell::{InteractiveShell, ReadResult}, | ||
ShellError, | ||
}; | ||
|
||
/// Represents a minimal shell capable of taking commands from standard input | ||
/// and reporting results to standard output and standard error streams. | ||
pub struct InteractiveShell { | ||
pub struct BasicShell { | ||
shell: brush_core::Shell, | ||
} | ||
|
||
impl InteractiveShell { | ||
impl BasicShell { | ||
/// Returns a new interactive shell instance, created with the provided options. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `options` - Options for creating the interactive shell. | ||
pub async fn new(options: &crate::Options) -> Result<InteractiveShell, ShellError> { | ||
pub async fn new(options: &crate::Options) -> Result<Self, ShellError> { | ||
let shell = brush_core::Shell::new(&options.shell).await?; | ||
Ok(InteractiveShell { shell }) | ||
Ok(Self { shell }) | ||
} | ||
} | ||
|
||
impl InteractiveShell for BasicShell { | ||
/// Returns an immutable reference to the inner shell object. | ||
pub fn shell(&self) -> &brush_core::Shell { | ||
&self.shell | ||
fn shell(&self) -> impl AsRef<brush_core::Shell> { | ||
self.shell.as_ref() | ||
} | ||
|
||
/// Returns a mutable reference to the inner shell object. | ||
pub fn shell_mut(&mut self) -> &mut brush_core::Shell { | ||
&mut self.shell | ||
} | ||
|
||
/// Runs the interactive shell loop, reading commands from standard input and writing | ||
/// results to standard output and standard error. Continues until the shell | ||
/// normally exits or until a fatal error occurs. | ||
pub async fn run_interactively(&mut self) -> Result<(), ShellError> { | ||
loop { | ||
// Check for any completed jobs. | ||
self.shell_mut().check_for_completed_jobs()?; | ||
|
||
let result = self.run_interactively_once().await; | ||
match result { | ||
Ok(Some(brush_core::ExecutionResult { | ||
exit_shell, | ||
return_from_function_or_script, | ||
.. | ||
})) => { | ||
if exit_shell { | ||
break; | ||
} | ||
|
||
if return_from_function_or_script { | ||
tracing::error!("return from non-function/script"); | ||
} | ||
} | ||
Ok(None) => { | ||
break; | ||
} | ||
Err(e) => { | ||
// Report the error, but continue to execute. | ||
tracing::error!("error: {:#}", e); | ||
} | ||
} | ||
} | ||
|
||
Ok(()) | ||
fn shell_mut(&mut self) -> impl AsMut<brush_core::Shell> { | ||
self.shell.as_mut() | ||
} | ||
|
||
async fn run_interactively_once( | ||
&mut self, | ||
) -> Result<Option<brush_core::ExecutionResult>, ShellError> { | ||
// Compose the prompt. | ||
let prompt = self.shell_mut().compose_prompt().await?; | ||
|
||
match self.readline(prompt.as_str()) { | ||
Some(read_result) => { | ||
let params = self.shell().default_exec_params(); | ||
match self.shell_mut().run_string(read_result, ¶ms).await { | ||
Ok(result) => Ok(Some(result)), | ||
Err(e) => Err(e.into()), | ||
} | ||
} | ||
None => Ok(None), | ||
} | ||
} | ||
|
||
fn readline(&mut self, prompt: &str) -> Option<String> { | ||
let _ = print!("{prompt}"); | ||
fn read_line(&mut self, prompt: &str) -> Result<ReadResult, ShellError> { | ||
print!("{prompt}"); | ||
let _ = std::io::stdout().flush(); | ||
|
||
let mut buffer = String::new(); | ||
let stdin = std::io::stdin(); // We get `Stdin` here. | ||
if let Ok(bytes_read) = stdin.read_line(&mut buffer) { | ||
if bytes_read > 0 { | ||
Some(buffer) | ||
Ok(ReadResult::Input(buffer)) | ||
} else { | ||
None | ||
Ok(ReadResult::Eof) | ||
} | ||
} else { | ||
None | ||
Err(ShellError::InputError) | ||
} | ||
} | ||
} | ||
|
||
/// Represents an error encountered while running or otherwise managing an interactive shell. | ||
#[derive(thiserror::Error, Debug)] | ||
pub enum ShellError { | ||
/// An error occurred with the embedded shell. | ||
#[error("{0}")] | ||
ShellError(#[from] brush_core::Error), | ||
|
||
/// A generic I/O error occurred. | ||
#[error("I/O error: {0}")] | ||
IoError(#[from] std::io::Error), | ||
fn update_history(&mut self) -> Result<(), ShellError> { | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/// Represents an error encountered while running or otherwise managing an interactive shell. | ||
#[allow(clippy::module_name_repetitions)] | ||
#[allow(clippy::enum_variant_names)] | ||
#[derive(thiserror::Error, Debug)] | ||
pub enum ShellError { | ||
/// An error occurred with the embedded shell. | ||
#[error("{0}")] | ||
ShellError(#[from] brush_core::Error), | ||
|
||
/// A generic I/O error occurred. | ||
#[error("I/O error: {0}")] | ||
IoError(#[from] std::io::Error), | ||
|
||
/// An error occurred while reading input. | ||
#[error("input error occurred")] | ||
InputError, | ||
|
||
/// The requested input backend type is not supported. | ||
#[error("requested input backend type not supported")] | ||
InputBackendNotSupported, | ||
} |
Oops, something went wrong.