use std::path::{Path, PathBuf};
use std::process::{Command, ExitCode};
const BUILTIN_SUBCOMMANDS: &[&str] = &["check", "diff", "lineage", "update", "help"];
pub(crate) fn is_builtin_subcommand(name: &str) -> bool {
BUILTIN_SUBCOMMANDS.contains(&name)
}
pub(crate) fn find_on_path(name: &str) -> Option<PathBuf> {
let path_var = std::env::var_os("PATH")?;
find_in_path_var(name, &path_var)
}
fn find_in_path_var(name: &str, path_var: &std::ffi::OsStr) -> Option<PathBuf> {
let binary_name = addon_binary_name(name);
std::env::split_paths(path_var)
.map(|dir| dir.join(&binary_name))
.find(|candidate| candidate.is_file())
}
#[cfg(not(target_os = "windows"))]
fn addon_binary_name(name: &str) -> String {
format!("zhao-{name}")
}
#[cfg(target_os = "windows")]
fn addon_binary_name(name: &str) -> String {
format!("zhao-{name}.exe")
}
pub(crate) fn dispatch(path: &Path, args: &[String]) -> ExitCode {
match Command::new(path).args(args).status() {
Ok(status) => match status.code() {
Some(code) => ExitCode::from(code as u8),
None => ExitCode::from(2),
},
Err(err) => {
eprintln!("error: could not run addon {}: {err}", path.display());
ExitCode::from(2)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_real_subcommand_is_recognized_as_builtin() {
for name in ["check", "diff", "lineage", "update", "help"] {
assert!(is_builtin_subcommand(name), "{name} should be builtin");
}
}
#[test]
fn an_addon_style_name_is_not_builtin() {
assert!(!is_builtin_subcommand("dbt-plan"));
}
#[test]
fn find_in_path_var_locates_a_matching_executable() {
let dir = tempfile::tempdir().expect("should create tempdir");
let binary_path = dir.path().join(addon_binary_name("dbt-plan"));
std::fs::write(&binary_path, "").expect("should write stub");
let path_var = std::env::join_paths([dir.path()]).expect("should join paths");
let found = find_in_path_var("dbt-plan", &path_var);
assert_eq!(found.as_deref(), Some(binary_path.as_path()));
}
#[test]
fn find_in_path_var_returns_none_when_nothing_matches() {
let dir = tempfile::tempdir().expect("should create tempdir");
let path_var = std::env::join_paths([dir.path()]).expect("should join paths");
assert_eq!(find_in_path_var("nonexistent-addon-xyz", &path_var), None);
}
#[test]
fn find_in_path_var_checks_every_directory_in_order() {
let dir_without = tempfile::tempdir().expect("should create tempdir");
let dir_with = tempfile::tempdir().expect("should create tempdir");
let binary_path = dir_with.path().join(addon_binary_name("dbt-plan"));
std::fs::write(&binary_path, "").expect("should write stub");
let path_var =
std::env::join_paths([dir_without.path(), dir_with.path()]).expect("should join paths");
let found = find_in_path_var("dbt-plan", &path_var);
assert_eq!(found.as_deref(), Some(binary_path.as_path()));
}
}