use std::{
env,
process::{self},
};
use bpaf::*;
use guartcl::{
GUARTCL_VERSION,
compile::{EmbeddedSources, TCL_SECTION},
guardian_interp,
};
use jimtcl::{
JimError, JimResult,
cli::{Jimsh, TclShellInvocation, jimsh_args},
tcl_error,
};
use log::debug;
#[derive(Debug, Clone)]
struct GuardianShell {
usage: bool,
shell: TclShellInvocation,
}
fn usage_switch() -> impl Parser<bool> {
short('U').long("usage").switch()
}
fn guarsh_parser() -> OptionParser<GuardianShell> {
construct!(GuardianShell {
usage(usage_switch()),
shell(jimsh_args()),
})
.to_options()
}
fn main() -> JimResult<process::ExitCode> {
env_logger::try_init().map_err(|_| tcl_error!("cannot initialize logging"))?;
let embedded = libsui::find_section(TCL_SECTION)
.map_err(|e| tcl_error!("error searching for Tcl section: {}", e))?;
let rc = if let Some(data) = embedded {
run_embedded(data)?
} else {
run_shell()?
};
let rc = u8::try_from(rc).map_err(|e| tcl_error!("out-of-bounds exit code {}", e))?;
Ok(rc.into())
}
fn run_embedded(data: &[u8]) -> JimResult<i32> {
let embed: EmbeddedSources =
serde_json::from_slice(data).map_err(|e| tcl_error!("embedded decode error: {}", e))?;
let interp = guardian_interp()?;
let mut args = env::args_os();
let argv0 = args.next();
let args: Vec<_> = args.collect();
interp.set_script_args(argv0, &args)?;
if embed.options.usage {
let args: Vec<_> = env::args().collect();
guartcl::usage::parse_args_source(&interp, &embed.source, &args)?;
}
debug!("running embedded script");
let result = interp.eval_source(&embed.name, 1, &embed.source);
interp.error_stack_trace(&result)?;
interp.resolve_exit(result)
}
fn run_shell() -> JimResult<i32> {
let argv0 = env::args_os().next();
let cli = guarsh_parser()
.version(
format!(
"{} (Jim version 0.{})",
GUARTCL_VERSION,
jimtcl::JIM_VERSION
)
.as_str(),
)
.run();
let interp = guardian_interp()?;
let jimsh = Jimsh::from_interp(interp, argv0)?;
if cli.usage
&& let Some(script) = cli.shell.script_file()
{
let mut args = Vec::with_capacity(cli.shell.args.len() + 1);
args.push(script.to_str().ok_or(JimError::InvalidUtf8)?.to_owned());
for arg in &cli.shell.args {
let arg = arg.to_str().ok_or(JimError::InvalidUtf8)?.to_owned();
args.push(arg);
}
guartcl::usage::parse_args(&jimsh.interp, script.as_ref(), &args)?;
}
jimsh.run(&cli.shell)
}