use std::path::Path;
use crate::domain::shell::Shell;
use crate::{resolve_config, resolve_config_opt, CmdOutcome, CmdResult, MAX_BIN_LEN};
pub(crate) fn validate_bin(bin: &str) -> Result<(), String> {
if bin.trim().is_empty() {
return Err("--bin must not be empty or whitespace-only".into());
}
if bin.len() > MAX_BIN_LEN {
return Err(format!(
"--bin is too long ({} bytes); maximum is {MAX_BIN_LEN}",
bin.len()
));
}
if bin.chars().any(|c| c.is_ascii_control() || c == '\u{0085}' || c == '\u{2028}' || c == '\u{2029}') {
return Err("--bin contains an invalid control character".into());
}
if bin.chars().any(|c| !c.is_ascii() || !c.is_ascii_graphic()) {
return Err("--bin must contain only printable ASCII characters".into());
}
Ok(())
}
pub(crate) fn handle(shell: String, bin: String, config_flag: Option<&Path>) -> CmdResult {
if let Err(msg) = validate_bin(&bin) {
eprintln!("error: {msg}");
return Ok(CmdOutcome::ExitCode(1));
}
let s: Shell = shell.parse().map_err(|e: crate::domain::shell::ShellParseError| {
Box::<dyn std::error::Error>::from(e.to_string())
})?;
let config = if config_flag.is_some() {
let (_path, cfg) = resolve_config(config_flag)?;
Some(cfg)
} else {
let (_path, cfg, _err) = resolve_config_opt(None);
cfg
};
let effective_bin = if matches!(s, Shell::Clink) && bin == "runex" {
std::env::current_exe()
.ok()
.and_then(|p| p.to_str().map(|s| s.to_string()))
.unwrap_or(bin)
} else {
bin
};
print!("{}", crate::app::shell_export::export_script(s, &effective_bin, config.as_ref()));
Ok(CmdOutcome::Ok)
}