use run_script::{IoOptions, ScriptOptions};
use std::collections::HashMap;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
pub async fn execute_async(
command: &str,
folder: Option<&str>,
stdout_fn: impl Fn(String) + Send + Sync + 'static,
stderr_fn: impl Fn(String) + Send + Sync + 'static,
complete_fn: impl Fn() + Send + Sync + 'static,
) -> anyhow::Result<bool> {
let mut options = ScriptOptions::new();
options.output_redirection = IoOptions::Pipe;
options.env_vars = Some(HashMap::from([("IS_TTY".to_string(), "true".to_string())]));
if folder.is_some() {
options.working_directory = Some(PathBuf::from(folder.unwrap()));
}
let args = vec![];
let child = run_script::spawn(command, &args, &options);
if child.is_err() {
let error = child.err().unwrap();
stderr_fn(error.to_string());
return Ok(false);
}
let mut child = child?;
let stdout = child.stdout.take().unwrap();
let stderr = child.stderr.take().unwrap();
let handle1 = tokio::spawn(async move {
let reader = BufReader::new(stdout);
for line in reader.lines() {
if let Ok(line) = line {
stdout_fn(line);
}
}
});
let handle2 = tokio::spawn(async move {
let reader = BufReader::new(stderr);
for line in reader.lines() {
if let Ok(line) = line {
stderr_fn(line);
}
}
});
tokio::try_join!(handle1, handle2)?;
let status = child.wait()?;
if status.success() {
complete_fn();
Ok(true)
} else {
Ok(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
use run_script::{IoOptions, ScriptOptions};
use std::collections::HashMap;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
#[tokio::test]
async fn test_command() {
let command = "pwd";
let result = execute_async(
command,
Some("/Users/wangbin/src/docs/docs-taiwu"),
|line| {
println!("stdout:{line}");
},
|line| {
println!("stdout:{line}");
},
|| {
println!("complete");
},
)
.await
.unwrap();
println!("result:{}", result);
}
#[tokio::test]
async fn test_command1() {
let result = execute_async(
"DOCKER_BUILDKIT=0 podman --help",
Some("/Users/wangbin/src/docs/docs-taiwu"),
|line| {
println!("stdout:{line}");
},
|line| {
println!("stdout:{line}");
},
|| {
println!("complete");
},
)
.await
.unwrap();
println!("result:{}", result);
}
#[tokio::test]
async fn test_command2() {
let result = execute_async(
"DOCKER_BUILDKIT=0 podman ps \"--help\"",
Some("/Users/wangbin/src/docs/docs-taiwu"),
|line| {
println!("stdout:{line}");
},
|line| {
println!("stdout:{line}");
},
|| {
println!("complete");
},
)
.await
.unwrap();
println!("result:{}", result);
}
#[test]
fn test_slice() {
let a = "\"--help\"";
println!("a:{a}");
let x = &a[1..a.len() - 1];
println!("x:{x}");
}
#[tokio::test]
async fn test_run_script1() -> anyhow::Result<()> {
let command = "antora --stacktrace antora-playbook.yml";
let mut options = ScriptOptions::new();
options.output_redirection = IoOptions::Pipe;
options.env_vars = Some(HashMap::from([("IS_TTY".to_string(), "true".to_string())]));
options.working_directory = Some(PathBuf::from("/Users/wangbin/src/docs/docs-taiwu"));
let args = vec![];
let result = run_script::spawn(command, &args, &options);
println!("result");
let mut child = result?;
let stdout = child.stdout.take().unwrap();
let stderr = child.stderr.take().unwrap();
let handle1 = tokio::spawn(async move {
let reader = BufReader::new(stdout);
for line in reader.lines() {
if let Ok(line) = line {
println!("stdout:{line}");
}
}
});
let handle2 = tokio::spawn(async move {
let reader = BufReader::new(stderr);
for line in reader.lines() {
if let Ok(line) = line {
println!("stderr:{line}");
}
}
});
tokio::try_join!(handle1, handle2)?;
let status = child.wait()?;
if status.success() {
println!("success");
} else {
println!("error");
}
Ok(())
}
#[tokio::test]
async fn test_run_script2() -> anyhow::Result<()> {
let result = execute_async(
"DOCKER_BUILDKIT=1 podman build \"--help\"",
Some("/Users/wangbin/src/docs/docs-taiwu"),
|line| {
println!("stdout:{line}");
},
|line| {
println!("stderr:{line}");
},
|| {
println!("complete");
},
)
.await;
println!("result:{result:?}");
Ok(())
}
}