use crate::{
error::Error,
session::{OsProcess, OsProcessStream},
Captures, Session,
};
use std::ops::{Deref, DerefMut};
#[cfg(not(feature = "async"))]
use crate::process::NonBlocking;
#[cfg(not(feature = "async"))]
use std::io::{Read, Write};
#[cfg(unix)]
use std::process::Command;
use crate::spawn;
#[cfg(feature = "async")]
use futures_lite::{AsyncRead, AsyncWrite};
#[cfg(unix)]
#[cfg(not(feature = "async"))]
pub fn spawn_bash() -> Result<ReplSession, Error> {
const DEFAULT_PROMPT: &str = "EXPECT_PROMPT";
let mut cmd = Command::new("bash");
let _ = cmd.env("PS1", DEFAULT_PROMPT);
let _ = cmd.env(
"PROMPT_COMMAND",
"PS1=EXPECT_PROMPT; unset PROMPT_COMMAND; bind 'set enable-bracketed-paste off'",
);
let session = crate::session::Session::spawn(cmd)?;
let mut bash = ReplSession::new(
session,
DEFAULT_PROMPT.to_string(),
Some("quit".to_string()),
false,
);
bash.expect_prompt()?;
Ok(bash)
}
#[cfg(unix)]
#[cfg(feature = "async")]
pub async fn spawn_bash() -> Result<ReplSession, Error> {
const DEFAULT_PROMPT: &str = "EXPECT_PROMPT";
let mut cmd = Command::new("bash");
let _ = cmd.env("PS1", DEFAULT_PROMPT);
let _ = cmd.env(
"PROMPT_COMMAND",
"PS1=EXPECT_PROMPT; unset PROMPT_COMMAND; bind 'set enable-bracketed-paste off'",
);
let session = crate::session::Session::spawn(cmd)?;
let mut bash = ReplSession::new(
session,
DEFAULT_PROMPT.to_string(),
Some("quit".to_string()),
false,
);
bash.expect_prompt().await?;
Ok(bash)
}
#[cfg(not(feature = "async"))]
pub fn spawn_python() -> Result<ReplSession, Error> {
let session = spawn("python")?;
let mut idle = ReplSession::new(session, ">>> ".to_owned(), Some("quit()".to_owned()), false);
idle.expect_prompt()?;
Ok(idle)
}
#[cfg(feature = "async")]
pub async fn spawn_python() -> Result<ReplSession, Error> {
let session = spawn("python")?;
let mut idle = ReplSession::new(session, ">>> ".to_owned(), Some("quit()".to_owned()), false);
idle.expect_prompt().await?;
Ok(idle)
}
#[cfg(windows)]
#[cfg(not(feature = "async"))]
pub fn spawn_powershell() -> Result<ReplSession, Error> {
const DEFAULT_PROMPT: &str = "EXPECTED_PROMPT>";
let session = spawn("pwsh -NoProfile -NonInteractive -NoLogo")?;
let mut powershell = ReplSession::new(
session,
DEFAULT_PROMPT.to_owned(),
Some("exit".to_owned()),
true,
);
let _ = powershell.execute(format!(
r#"function prompt {{ "{}"; return " " }}"#,
DEFAULT_PROMPT
))?;
let _ =
powershell.execute(r#"[System.Environment]::SetEnvironmentVariable("TERM", "dumb")"#)?;
let _ = powershell
.execute(r#"[System.Environment]::SetEnvironmentVariable("TERM", "NO_COLOR")"#)?;
Ok(powershell)
}
#[cfg(windows)]
#[cfg(feature = "async")]
pub async fn spawn_powershell() -> Result<ReplSession, Error> {
const DEFAULT_PROMPT: &str = "EXPECTED_PROMPT>";
let session = spawn("pwsh -NoProfile -NonInteractive -NoLogo")?;
let mut powershell = ReplSession::new(
session,
DEFAULT_PROMPT.to_owned(),
Some("exit".to_owned()),
true,
);
let _ = powershell
.execute(format!(
r#"function prompt {{ "{}"; return " " }}"#,
DEFAULT_PROMPT
))
.await?;
let _ = powershell
.execute(r#"[System.Environment]::SetEnvironmentVariable("TERM", "dumb")"#)
.await?;
let _ = powershell
.execute(r#"[System.Environment]::SetEnvironmentVariable("TERM", "NO_COLOR")"#)
.await?;
Ok(powershell)
}
#[derive(Debug)]
pub struct ReplSession<P = OsProcess, S = OsProcessStream> {
prompt: String,
session: Session<P, S>,
quit_command: Option<String>,
is_echo_on: bool,
}
impl<P, S> ReplSession<P, S> {
pub fn new(
session: Session<P, S>,
prompt: String,
quit_command: Option<String>,
is_echo: bool,
) -> Self {
Self {
session,
prompt,
quit_command,
is_echo_on: is_echo,
}
}
pub fn get_prompt(&self) -> &str {
&self.prompt
}
pub fn get_quit_command(&self) -> Option<&str> {
self.quit_command.as_deref()
}
pub fn is_echo(&self) -> bool {
self.is_echo_on
}
pub fn into_session(self) -> Session<P, S> {
self.session
}
}
#[cfg(not(feature = "async"))]
impl<P, S: Read + NonBlocking> ReplSession<P, S> {
pub fn expect_prompt(&mut self) -> Result<(), Error> {
let _ = self._expect_prompt()?;
Ok(())
}
fn _expect_prompt(&mut self) -> Result<Captures, Error> {
self.session.expect(&self.prompt)
}
}
#[cfg(feature = "async")]
impl<P, S: AsyncRead + Unpin> ReplSession<P, S> {
pub async fn expect_prompt(&mut self) -> Result<(), Error> {
let _ = self._expect_prompt().await?;
Ok(())
}
async fn _expect_prompt(&mut self) -> Result<Captures, Error> {
self.session.expect(&self.prompt).await
}
}
#[cfg(not(feature = "async"))]
impl<P, S: Read + NonBlocking + Write> ReplSession<P, S> {
pub fn execute<SS: AsRef<str> + Clone>(&mut self, cmd: SS) -> Result<Vec<u8>, Error> {
self.send_line(cmd)?;
let found = self._expect_prompt()?;
Ok(found.before().to_vec())
}
#[cfg(not(feature = "async"))]
pub fn send_line<Text: AsRef<str>>(&mut self, line: Text) -> Result<(), Error> {
let text = line.as_ref();
self.session.send_line(text)?;
if self.is_echo_on {
let _ = self.expect(line.as_ref())?;
}
Ok(())
}
pub fn exit(&mut self) -> Result<(), Error> {
if let Some(quit_command) = &self.quit_command {
self.session.send_line(quit_command)?;
}
Ok(())
}
}
#[cfg(feature = "async")]
impl<P, S: AsyncRead + AsyncWrite + Unpin> ReplSession<P, S> {
pub async fn execute(&mut self, cmd: impl AsRef<str>) -> Result<Vec<u8>, Error> {
self.send_line(cmd).await?;
let found = self._expect_prompt().await?;
Ok(found.before().to_vec())
}
pub async fn send_line(&mut self, line: impl AsRef<str>) -> Result<(), Error> {
self.session.send_line(line.as_ref()).await?;
if self.is_echo_on {
let _ = self.expect(line.as_ref()).await?;
}
Ok(())
}
pub async fn exit(&mut self) -> Result<(), Error> {
if let Some(quit_command) = &self.quit_command {
self.session.send_line(quit_command).await?;
}
Ok(())
}
}
impl<P, S> Deref for ReplSession<P, S> {
type Target = Session<P, S>;
fn deref(&self) -> &Self::Target {
&self.session
}
}
impl<P, S> DerefMut for ReplSession<P, S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.session
}
}