use std::collections::HashMap;
use crate::core::config::output::TestConfig;
use crate::core::config::tools::ToolsConfig;
use crate::core::error::AlefError;
pub(super) fn validate_section<C, F, P>(
section: &str,
table: &HashMap<String, C>,
main_fields: F,
precondition: P,
) -> Result<(), AlefError>
where
F: Fn(&C) -> Vec<&'static str>,
P: Fn(&C) -> Option<&str>,
{
for (lang, cfg) in table {
let main = main_fields(cfg);
if !main.is_empty() && precondition(cfg).is_none() {
let fields = main.iter().map(|f| format!("`{f}`")).collect::<Vec<_>>().join("/");
return Err(AlefError::Config(format!(
"[{section}.{lang}] sets a main command ({fields}) without `precondition`. Add a POSIX \
check such as `precondition = \"command -v <tool> >/dev/null 2>&1\"`."
)));
}
}
Ok(())
}
pub(super) fn test_main_fields(c: &TestConfig) -> Vec<&'static str> {
let mut v = Vec::new();
if c.command.is_some() {
v.push("command");
}
if c.coverage.is_some() {
v.push("coverage");
}
v
}
pub(super) fn validate_test_e2e_precondition(table: &HashMap<String, TestConfig>) -> Result<(), AlefError> {
for (lang, cfg) in table {
if cfg.e2e.is_some() && cfg.precondition.is_none() && cfg.e2e_precondition.is_none() {
return Err(AlefError::Config(format!(
"[test.{lang}] sets `e2e` without `precondition` or `e2e_precondition`. Prefer \
`e2e_precondition`, scoped to what the `e2e` command itself needs: \
`e2e_precondition = \"command -v <tool> >/dev/null 2>&1\"`."
)));
}
}
Ok(())
}
pub(super) fn validate_tools(tools: &ToolsConfig) -> Result<(), AlefError> {
if let Some(pm) = tools.python_package_manager.as_deref() {
ensure_well_formed_tool_name("tools.python_package_manager", pm)?;
}
if let Some(pm) = tools.node_package_manager.as_deref() {
ensure_well_formed_tool_name("tools.node_package_manager", pm)?;
}
if let Some(list) = tools.rust_dev_tools.as_deref() {
for tool in list {
ensure_well_formed_tool_name("tools.rust_dev_tools[]", tool)?;
}
}
Ok(())
}
fn is_well_formed_tool_char(c: char) -> bool {
c.is_ascii_alphanumeric() || matches!(c, '-' | '_' | '.')
}
fn ensure_well_formed_tool_name(field: &str, value: &str) -> Result<(), AlefError> {
if value.is_empty() || !value.chars().all(is_well_formed_tool_char) {
return Err(AlefError::Config(format!(
"{field} = {value:?} is not a well-formed tool name. \
Tool names must match `[A-Za-z0-9._-]+` (single executable, no spaces or shell metacharacters)."
)));
}
Ok(())
}