use super::*;
use std::process::{Child, Command, Stdio};
use std::collections::HashMap;
pub struct Cmd {
pub env_vars: HashMap<String, PathBuf>,
pub wrk_dir: PathBuf,
pub cmd: PathBuf,
pub input: String,
}
impl Cmd {
fn create_command(&self, script: &Path) -> std::process::Command {
debug!("run script: {:?}", script);
let mut command = Command::new(script);
for (k, v) in &self.env_vars {
trace!("env {k:?} = {v:?}");
}
command.current_dir(&self.wrk_dir).envs(&self.env_vars);
command
}
pub fn run_with_input(&self) -> Result<String> {
let mut child = self
.create_command(&self.cmd)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.with_context(|| format!("Failed to run script: {:?}", &self.cmd))?;
child
.stdin
.as_mut()
.context("Failed to open stdin")?
.write_all(self.input.as_bytes())
.context("Failed to write to stdin")?;
let output = child.wait_with_output().context("Failed to read stdout")?;
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
pub fn create_child_process(&self) -> Result<Child> {
let child = self
.create_command(&self.cmd)
.spawn()
.with_context(|| format!("Failed to run main script: {:?}", &self.cmd))?;
Ok(child)
}
}
impl Cmd {
pub fn bash_script(&self) -> String {
let wrk_dir = self.wrk_dir.shell_escape_lossy();
let cmd = &self.cmd.shell_escape_lossy();
let export_env: String = self
.env_vars
.iter()
.map(|(var, value)| {
let value = value.shell_escape_lossy();
format!("export {var}={value}\n")
})
.collect();
let input = &self.input;
format!(
"#cd {wrk_dir}
{export_env}
{cmd} <<EOF
{input}
EOF
"
)
}
pub fn generate_bash_script(&self, path: &Path) -> Result<()> {
let script = self.bash_script();
gut::fs::write_script_file(path, &script)?;
Ok(())
}
}
impl BlackBoxModel {
#[cfg(feature = "adhoc")]
pub fn bash_script_for_execution(&mut self, mol: &Molecule) -> Result<String> {
let txt = self.render_input(&mol)?;
let mut cmd = self.create_onetime_cmd(&txt)?;
cmd.cmd = self.run_file.to_owned();
cmd.wrk_dir = self.run_file.parent().unwrap().to_owned();
let input = self.render_input(mol)?;
Ok(cmd.bash_script())
}
}
impl BlackBoxModel {
fn create_onetime_cmd(&mut self, text: &str) -> Result<Cmd> {
let run_file = self.prepare_compute_env()?;
let mut env_vars = vec![];
let tpl_dir = self
.tpl_file
.parent()
.ok_or(format_err!("bbm_tpl_file: invalid path: {:?}", self.tpl_file))?
.to_owned();
env_vars.push(("BBM_TPL_DIR".into(), tpl_dir));
let job_dir = std::env::current_dir()?;
env_vars.push(("BBM_JOB_DIR".into(), job_dir));
let cmdline = format!("{}", run_file.display());
debug!("submit cmdline: {}", cmdline);
let wrk_dir = run_file.parent().unwrap().to_owned();
let env_vars = env_vars.into_iter().collect();
let cmd = run_file.to_owned();
let cmd = Cmd { cmd, env_vars, wrk_dir, input: text.into() };
Ok(cmd)
}
pub(super) fn submit_cmd(&mut self, text: &str) -> Result<String> {
let mut cmd = self.create_onetime_cmd(text)?;
let out = if let Some(int_file) = &self.int_file {
debug!("interactive mode enabled");
if self.task.is_none() {
let child = cmd.create_child_process()?;
self.task = Task(child).into();
}
cmd.cmd = int_file.to_owned();
cmd.run_with_input()?
} else {
cmd.run_with_input()?
};
Ok(out)
}
}