use super::{Problems, kebab, variable_name};
use crate::model::Spec;
use crate::placeholders::{self, Piece};
pub(super) fn version_and_name(spec: &Spec, p: &mut Problems) {
if spec.version != 1 {
p.add("version", format!("unsupported version {}; this isoloom reads version 1", spec.version));
}
if !kebab(&spec.name) {
p.add("name", "use kebab-case: lowercase letters, digits and dashes (e.g. supplier-portal-api)");
} else if spec.name.len() > 55 {
p.add("name", "at most 55 characters: it becomes the Kubernetes namespace `isoloom-<name>`");
}
}
pub(super) fn inputs(spec: &Spec, p: &mut Problems) {
for (i, input) in spec.inputs.iter().enumerate() {
if !variable_name(input) {
p.add(
format!("inputs[{i}]"),
format!("`{input}`: inputs are environment variable names (letters, digits and `_`, not starting with a digit)"),
);
}
}
}
pub(super) fn message(spec: &Spec, p: &mut Problems) {
let Some(m) = &spec.message else { return };
for piece in placeholders::pieces(m) {
match piece {
Piece::Text(_) => {}
Piece::Placeholder(inner) => {
let path = inner.trim();
if path.is_empty() || path.split('.').any(str::is_empty) {
p.add("message", format!("`{{{{ {path} }}}}` isn't a path like machines.web.addresses.front"));
}
}
Piece::Unclosed(_) => p.add("message", "a `{{` without its `}}`"),
}
}
}
#[cfg(test)]
mod tests {
use crate::testing::{lab, problems};
const WEB: &str = "machines: { web: { networks: { lan: 10 }, docker: { image: nginx } } }\n";
fn named(name: &str) -> Vec<String> {
problems(&format!("version: 1\nname: {name}\nnetworks: {{ lan: {{ cidr: 10.0.0.0/24 }} }}\n{WEB}"))
}
fn message(message: &str) -> Vec<String> {
problems(&lab(&format!("{WEB}message: '{message}'\n")))
}
#[test]
fn a_name_is_kebab_case_and_fits_a_kubernetes_namespace() {
assert_eq!(named(&"a".repeat(55)), Vec::<String>::new());
assert_eq!(
named(&"a".repeat(56)),
["name: at most 55 characters: it becomes the Kubernetes namespace `isoloom-<name>`"]
);
assert_eq!(
named("Portal"),
["name: use kebab-case: lowercase letters, digits and dashes (e.g. supplier-portal-api)"]
);
}
#[test]
fn inputs_are_environment_variable_names() {
let found = problems(&lab(&format!("inputs: [_TOKEN, A_1, 1A, A-B]\n{WEB}")));
let why = |i: usize, input: &str| {
format!("inputs[{i}]: `{input}`: inputs are environment variable names (letters, digits and `_`, not starting with a digit)")
};
assert_eq!(found, [why(2, "1A"), why(3, "A-B")]);
}
#[test]
fn a_messages_placeholders_are_closed_paths() {
assert_eq!(message("Open {{ machines.web.addresses.lan }}"), Vec::<String>::new());
for (bad, path) in [
("{{ }}", ""),
("{{ machines..web }}", "machines..web"),
("{{ machines.web. }}", "machines.web."),
] {
let why = format!("message: `{{{{ {path} }}}}` isn't a path like machines.web.addresses.front");
assert_eq!(message(&format!("Open {bad}")), [why], "{bad}");
}
assert_eq!(message("Open {{ machines.web"), ["message: a `{{` without its `}}`"]);
}
}