use anyhow::{Result, bail};
use super::schema::{CommandTemplate, EnvironmentOverride, OperationTemplate, ResultTemplate};
pub fn resolve_active_env<'a>(
cli_env: Option<&'a str>,
config_env: Option<&'a str>,
template_default: Option<&'a str>,
) -> Option<&'a str> {
cli_env.or(config_env).or(template_default)
}
pub fn validate_env_name(name: &str) -> Result<()> {
if name.is_empty() || name.len() > 64 {
bail!("environment name must be 1–64 characters, got `{name}`");
}
if !name
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
{
bail!(
"environment name `{name}` contains invalid characters; \
only alphanumeric, underscore, and hyphen are allowed"
);
}
Ok(())
}
pub fn select_for_env<'a>(
cmd: &'a CommandTemplate,
env_name: Option<&str>,
) -> (&'a OperationTemplate, &'a ResultTemplate) {
let Some(name) = env_name else {
return (&cmd.operation, &cmd.result);
};
match cmd.environment_overrides.get(name) {
Some(EnvironmentOverride {
operation,
result: Some(result),
}) => (operation, result),
Some(EnvironmentOverride {
operation,
result: None,
}) => (operation, &cmd.result),
None => (&cmd.operation, &cmd.result),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolve_prefers_cli_flag() {
assert_eq!(
resolve_active_env(Some("staging"), Some("prod"), Some("dev")),
Some("staging")
);
}
#[test]
fn resolve_falls_back_to_config() {
assert_eq!(
resolve_active_env(None, Some("prod"), Some("dev")),
Some("prod")
);
}
#[test]
fn resolve_falls_back_to_template() {
assert_eq!(resolve_active_env(None, None, Some("dev")), Some("dev"));
}
#[test]
fn resolve_returns_none_when_nothing_set() {
assert_eq!(resolve_active_env(None, None, None), None);
}
#[test]
fn alphanumeric_name_is_valid() {
assert!(validate_env_name("production").is_ok());
}
#[test]
fn name_with_hyphens_is_valid() {
assert!(validate_env_name("staging-eu").is_ok());
}
#[test]
fn name_with_underscores_and_digits_is_valid() {
assert!(validate_env_name("dev_local_2").is_ok());
}
#[test]
fn empty_name_returns_error() {
assert!(validate_env_name("").is_err());
}
#[test]
fn name_with_spaces_returns_error() {
assert!(validate_env_name("has space").is_err());
}
#[test]
fn name_with_path_traversal_returns_error() {
assert!(validate_env_name("../etc/passwd").is_err());
}
#[test]
fn name_exceeding_max_length_returns_error() {
assert!(validate_env_name(&"a".repeat(65)).is_err());
}
}