Her review: too much text, and an assertion helper instead of that much code where the file should just say "this option cannot contain that". `lib/name-guards.nix` gets `mustNotEqual` / `mustNotContain`; both call sites in swarm-otel become four fields each — the option, the names, the list, and the sentence explaining the failure. The message plumbing (find every offender, quote them, name the list) is one shape shared by both. Offenders are printed after their label rather than before it, because no English verb agrees with both one name and five. Converted the pre-existing equality assertion too. Leaving one hand-rolled beside one helper-built is the worse of the two outcomes, and it is the same guard. Verified by evaluating both guards directly rather than only parsing: clean rosters pass, `foo-agent`/`beehive` fail containment, `swarm` fails equality, and `forgeworks`/`operator-hq` still pass — the control that keeps the two lists from being merged.
66 lines
1.5 KiB
Nix
66 lines
1.5 KiB
Nix
# Assertions about names an operator chooses, so a module states WHAT is
|
|
# forbidden and not how to phrase the refusal.
|
|
{ lib }:
|
|
let
|
|
# One shape for both guards. Reports EVERY offender, not the first: fixing
|
|
# one per rebuild is the slowest possible way to learn a rule.
|
|
#
|
|
# Offenders come after their label rather than before it, so one name and
|
|
# five read the same — no English verb agrees with both.
|
|
guard =
|
|
{
|
|
option,
|
|
names,
|
|
forbidden,
|
|
hit,
|
|
problem,
|
|
forbiddenLabel,
|
|
why,
|
|
}:
|
|
let
|
|
offenders = lib.filter (n: lib.any (f: hit f n) forbidden) names;
|
|
quote = xs: lib.concatMapStringsSep ", " (x: "'${x}'") xs;
|
|
in
|
|
{
|
|
assertion = offenders == [ ];
|
|
message = ''
|
|
${option} ${problem}: ${quote offenders}
|
|
${forbiddenLabel}: ${quote forbidden}
|
|
|
|
${why}
|
|
'';
|
|
};
|
|
in
|
|
{
|
|
# No name may BE one of `reserved`.
|
|
mustNotEqual =
|
|
{
|
|
option,
|
|
names,
|
|
reserved,
|
|
why,
|
|
}:
|
|
guard {
|
|
inherit option names why;
|
|
forbidden = reserved;
|
|
hit = f: n: f == n;
|
|
problem = "has reserved name(s)";
|
|
forbiddenLabel = "Reserved";
|
|
};
|
|
|
|
# No name may CONTAIN one of `fragments`.
|
|
mustNotContain =
|
|
{
|
|
option,
|
|
names,
|
|
fragments,
|
|
why,
|
|
}:
|
|
guard {
|
|
inherit option names why;
|
|
forbidden = fragments;
|
|
hit = lib.hasInfix;
|
|
problem = "has name(s) containing a reserved word";
|
|
forbiddenLabel = "Forbidden as substrings";
|
|
};
|
|
}
|