use anyhow::{Context, Result, bail};
use clap::Args;
use console::style;
use std::process::Command;
use beru_manifest::BeruManifest;
#[derive(Debug, Args)]
pub struct RunArgs {
#[arg(long, default_value = "debug")]
pub profile: String,
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
pub args: Vec<String>,
}
pub fn exec(args: RunArgs) -> Result<()> {
let project_dir = std::env::current_dir().context("failed to get current directory")?;
let manifest = BeruManifest::from_dir(&project_dir).context("failed to parse Beru.toml")?;
if manifest.package.package_type != beru_manifest::PackageType::Executable {
bail!(
"`beru run` is only for executable projects. This project is type '{}'.",
manifest.package.package_type
);
}
let build_args = super::build::BuildArgs {
profile: args.profile.clone(),
};
super::build::exec(build_args)?;
let target_name = manifest.package.name.replace('-', "_");
let build_dir = project_dir.join("build");
let exe_path = find_executable(&build_dir, &target_name)?;
println!(
" {} `{}`\n",
style("Running").green().bold(),
manifest.package.name,
);
let status = Command::new(&exe_path)
.args(&args.args)
.status()
.with_context(|| format!("failed to run {}", exe_path.display()))?;
if !status.success() {
std::process::exit(status.code().unwrap_or(1));
}
Ok(())
}
fn find_executable(build_dir: &std::path::Path, name: &str) -> Result<std::path::PathBuf> {
let candidates = [
build_dir.join(name),
build_dir.join(format!("{name}.exe")),
build_dir.join("Debug").join(name),
build_dir.join("Release").join(name),
];
for candidate in &candidates {
if candidate.exists() {
return Ok(candidate.clone());
}
}
if let Some(found) = find_file_recursive(build_dir, name) {
return Ok(found);
}
bail!(
"could not find executable '{}' in {}. Was the build successful?",
name,
build_dir.display()
)
}
fn find_file_recursive(dir: &std::path::Path, name: &str) -> Option<std::path::PathBuf> {
let entries = std::fs::read_dir(dir).ok()?;
for entry in entries.flatten() {
let path = entry.path();
if path.is_file() {
if let Some(file_name) = path.file_name().and_then(|n| n.to_str())
&& (file_name == name || file_name == format!("{name}.exe"))
{
return Some(path);
}
} else if path.is_dir()
&& let Some(found) = find_file_recursive(&path, name)
{
return Some(found);
}
}
None
}