# 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"; }; }