use std::io::{BufRead, Write};
use crate::cli::argv::{Opt, parse_args, read_stdin};
use crate::cli::context::Cli;
use crate::cli::help::{render_help_for, wants_help};
use crate::cli::output::{EXIT_OK, Io, Result};
use crate::cli::render::Style;
use crate::cli::shell::session::ShellSession;
fn emit_prompt(prompt: &str) {
let mut h = std::io::stdout().lock();
let _ = h.write_all(prompt.as_bytes());
let _ = h.flush();
}
fn read_line() -> Option<String> {
let mut line = String::new();
match std::io::stdin().lock().read_line(&mut line) {
Ok(0) | Err(_) => None,
Ok(_) => {
while line.ends_with('\n') || line.ends_with('\r') {
line.pop();
}
Some(line)
}
}
}
fn run_script(session: &mut ShellSession<'_>) -> i32 {
let script = read_stdin();
let mut code = EXIT_OK;
for line in script.split('\n') {
let c = session.exec(line.strip_suffix('\r').unwrap_or(line));
if c != EXIT_OK {
code = c;
}
if session.exited {
break;
}
}
code
}
fn run_prompted(session: &mut ShellSession<'_>, prompt: &str) -> i32 {
let mut code = EXIT_OK;
emit_prompt(prompt);
while let Some(line) = read_line() {
let c = session.exec(&line);
if c != EXIT_OK {
code = c;
}
if session.exited {
return code;
}
emit_prompt(prompt);
}
code
}
fn run_interactive(
style: Style,
io: Io,
session: &mut ShellSession<'_>,
prompt: Option<&str>,
) -> i32 {
let prompt = prompt.map_or_else(
|| {
if style.plain() {
"omg> ".to_owned()
} else {
format!("{}{}", style.accent("omg"), style.dim("> "))
}
},
str::to_owned,
);
io.err(&style.dim(" omg shell — type `?` for session-binding help, `exit` to leave"));
loop {
emit_prompt(&prompt);
let Some(line) = read_line() else {
io.err("");
return EXIT_OK;
};
session.exec(&line);
if session.exited {
return EXIT_OK;
}
}
}
pub fn shell(cli: &mut Cli, args: &[String]) -> Result<i32> {
if wants_help(args) {
return render_help_for(cli, "shell");
}
let a = parse_args(args, &[Opt::value("prompt")])?;
let prompt = a.value("prompt").map(str::to_owned).or_else(|| {
std::env::var("OMG_SHELL_PROMPT")
.ok()
.filter(|s| !s.is_empty())
});
if cli.flags.server.is_none() {
cli.workspace()?;
}
let interactive = cli.io.stdout_tty() && cli.io.stdin_tty();
let (style, io) = (cli.style, cli.io);
let mut session = ShellSession::new(cli);
if !interactive {
return Ok(match prompt {
Some(p) => run_prompted(&mut session, &p),
None => run_script(&mut session),
});
}
Ok(run_interactive(style, io, &mut session, prompt.as_deref()))
}