use std::{env, path::PathBuf};
pub fn bin_path(name: &str) -> Option<PathBuf> {
env::var(bin_file_env_var(name)).ok().map(Into::into)
}
fn bin_file_env_var(name: &str) -> String {
format!("CARGO_BIN_FILE_{}", crate_env_name(name))
}
fn crate_env_name(name: &str) -> String {
name.replace("-", "_").to_ascii_uppercase()
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Mutex, OnceLock};
fn env_lock() -> &'static Mutex<()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(()))
}
#[test]
fn crate_name_is_normalized_for_env_var_suffix() {
assert_eq!(crate_env_name("my-crate"), "MY_CRATE");
assert_eq!(crate_env_name("already_UPPER"), "ALREADY_UPPER");
}
#[test]
fn crate_name_maps_to_cargo_bin_file_env_var() {
assert_eq!(
bin_file_env_var("my-crate"),
"CARGO_BIN_FILE_MY_CRATE".to_string()
);
}
#[test]
fn returns_none_when_env_var_is_missing() {
let _guard = env_lock().lock().unwrap();
let key = bin_file_env_var("missing-crate");
unsafe {
env::remove_var(&key);
}
assert_eq!(bin_path("missing-crate"), None);
}
#[test]
fn returns_path_from_matching_env_var() {
let _guard = env_lock().lock().unwrap();
let key = bin_file_env_var("example-crate");
let value = "/tmp/example-artifact";
unsafe {
env::set_var(&key, value);
}
assert_eq!(
bin_path("example-crate"),
Some(PathBuf::from(value))
);
unsafe {
env::remove_var(&key);
}
}
}