use std::io::{self, IsTerminal};
use clap::Parser;
use crate::error::ExitCode;
use crate::{Interp, JimObject, JimResult};
use crate::{JimError, sys};
#[derive(Parser, Debug)]
#[command(disable_version_flag = true)]
pub struct Jimsh {
#[arg(long = "version")]
pub print_version: bool,
#[arg(short = 'e')]
pub expression: Option<String>,
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
pub script: Vec<String>,
}
impl Jimsh {
pub fn script_file(&self) -> Option<&str> {
self.script.first().map(String::as_str)
}
pub fn script_args(&self) -> &[String] {
if !self.script.is_empty() && self.expression.is_none() {
&self.script[1..]
} else {
&self.script
}
}
pub fn setup_env(&self, interp: &Interp) -> JimResult<()> {
interp.eval_source("initjimsh.tcl", 1, sys::tclexts::initjimsh)?;
if let Some(path) = self.script_file() {
interp.set_variable("argv0", path)?;
}
let argv = JimObject::empty(interp);
for arg in self.script_args() {
argv.list_append(arg.as_str());
}
interp.set_variable("argv", argv)?;
interp.set_variable("jim::lineedit", 0)?;
Ok(())
}
pub fn run(&self, interp: &Interp) -> JimResult<i32> {
let result = if let Some(script) = &self.expression {
interp.eval(script).map(|res| {
println!("{}", res);
})
} else if let Some(path) = self.script_file() {
if path == "-" {
interp
.eval("eval [info source [stdin read] stdin 1]")
.map(|_| ())
} else {
interp.eval_file(path)
}
} else if io::stdin().is_terminal() {
interp.interactive_prompt()
} else {
interp
.eval("eval [info source [stdin read] stdin 1]")
.map(|_| ())
};
if let Err(JimError::Error(msg)) = &result {
eprintln!("{}", msg);
let stack = interp.stack_trace();
let obj = interp.new_string("stackdump");
obj.list_append(stack);
let result = interp.eval_object(&obj)?;
eprintln!("{}", result);
Ok(1)
} else if let Err(JimError::OtherCode(ExitCode::Break)) = &result {
Ok(0)
} else {
interp.resolve_exit(result)
}
}
}