jimtcl 0.4.0-beta4

Embed Jim Tcl in Rust.
Documentation
//! Command-line shells.

use clap::Parser;

use crate::{Interp, JimObject, JimResult};
use crate::{JimError, sys};

/// Rust wrapper to implement jimsh
#[derive(Parser, Debug)]
#[command(version)]
pub struct Jimsh {
    /// Tcl script to execute, optionally with arguments.
    #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
    script: Vec<String>,
}

impl Jimsh {
    /// Run the command line in the specified interpreter.  Returns the Jim
    /// exit code on success.
    pub fn run(&self, interp: &Interp) -> JimResult<i32> {
        interp.eval_source("initjimsh.tcl", 1, sys::tclexts::initjimsh)?;
        let result = if self.script.len() >= 1 {
            let path = &self.script[0];
            interp.set_variable("argv0", path.as_str())?;
            let argv = JimObject::empty(interp);
            for arg in &self.script[1..] {
                argv.list_append(arg.as_str());
            }
            interp.set_variable("argv", argv)?;
            interp.eval_file(path)
        } else {
            interp.set_variable("argv", "")?;
            interp.interactive_prompt()
        };

        // special-case unhandled errors to get stacktrace
        if let Err(JimError::Error(msg)) = &result {
            eprintln!("Tcl script failed: {}", 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 {
            interp.resolve_exit(result)
        }
    }
}