Documentation
¶
Overview ¶
Package enumguard is the recurrence guard for restating a value the SDK already generates.
Enum values and machine-readable error codes must come from the SDK's generated constants, never from a string literal — see STYLE_GUIDE.md §"Enum values and error codes come from the SDK, not from literals". That rule failed twice by review, so every package declaring such values pins it with a test rather than trusting a reader to notice.
The check parses the package's own source rather than taking a hand-written list, so it cannot drift. It fires in both directions: on a literal that the SDK covers today, and — through Check's Absent map — on a literal that a future SDK release promotes into an enum.
It walks const *and* var declarations, including the elements of slice and map composite literals, because a restated enum is far more often a `var validFoos = []string{...}` feeding a OneOf validator than a lone const. It also reaches inside function bodies for three shapes. Two are the framework calls that declare an accepted set in all but name — stringvalidator.OneOf and stringdefault.StaticString — since a schema that inlines its vocabulary is restating it just as surely as a package-level var would. The third is a short variable declaration, `v := "install"`, which is how a builder names the one wire value it is about to send. That third shape was missing until this package's own review found five shipped packages whose only restated enum had it, leaving those guards unable to protect their own source.
It reads every non-test file in the package directory, so moving a declaration between files cannot slip past it.
What it deliberately does not cover: a literal that is not declared but merely used — compared against in ordinary logic (`if cdnType == "AMAZON_S3"`, `case "PENDING":`), returned (`return "PENDING"`), passed as an argument to anything but the two framework calls above, or assigned to a field of a struct literal built inside a function. Reaching those would mean treating every string in the package as an enum reference, which fires on prose and on unrelated vocabularies that happen to share a spelling. Those are an audit's job, not a guard's. The line the walker draws is declaration versus use: a short variable declaration is collected because the name it binds is the value the code goes on to send, whereas the same literal one line later in a `case` is only being recognised.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Findings ¶
type Findings struct {
// Restated are literals the SDK already provides a constant for.
Restated []string
// Promoted are Absent entries the SDK has since started generating.
Promoted []string
// Stale are Absent or Ignore entries no literal in the package uses.
Stale []string
// Vacuous is set when no literal could ever be reported, for either of the
// two reasons that happens: Covered is empty, or every value in it is
// exempted. A guard that cannot fail is worse than none — it reads as
// coverage while asserting nothing — so this is a failure. The empty case
// is the one an SDK release causes on its own, by withdrawing the last
// vocabulary a package restated, and it must not pass silently.
Vacuous string
// Examined is how many string literals were parsed.
Examined int
}
Findings is what Check returns: violations to fail on, plus the count of literals actually examined so a caller can refuse a vacuous pass.
type Literal ¶
type Literal struct {
// Decl names the declaration the literal was found under: a const or var
// name, or "func -> name" for a literal declared inside a function body.
Decl string
// Value is the literal's unquoted content.
Value string
// Pos is "file:line", relative to the package directory.
Pos string
}
Literal is one string literal found in a declaration the package makes — a const, a var, an inlined framework set or a func-body short variable declaration — tagged with that declaration so a failure names something a reader can grep for.
type Params ¶
type Params struct {
// Dir is the package directory to parse. Empty means ".".
Dir string
// Covered is every value the SDK generates for the vocabularies this
// package writes — the concatenation of the relevant *Values() helpers.
// A literal appearing here is a violation unless Absent excuses it.
//
// Leaving it empty is itself reported as [Findings.Vacuous]: a caller that
// names no vocabulary asserts nothing, so a package with no vocabulary left
// to restate must record the gap and drop its test rather than keep a test
// that cannot fail.
Covered []string
// Absent maps a value this package must restate to the reason the SDK
// carries no constant for it. Stating the reason per value is the point:
// a comment claiming "the SDK has none of these" is the exact shape of
// the defect this guard exists to catch, because it is usually true of
// some values and false of others.
//
// An entry that Covered turns out to contain is itself a failure — that
// is how the guard reports an SDK release promoting a literal into an
// enum.
Absent map[string]string
// Ignore lists literal values that are not members of any vocabulary in
// Covered, even though they spell one. Two shapes: something that is not
// an enum value at all — an attribute name, a map key, a sentinel, an
// English noun — and a genuine member of a *different* vocabulary that
// the SDK does not generate. Each entry needs a reason for the same
// reason Absent does.
//
// The distinction from Absent matters: Absent asserts the SDK carries no
// constant for the value and is therefore checked against Covered, so an
// SDK release that starts generating it fails. Ignore asserts the value
// belongs to a different set, which a foreign vocabulary gaining or
// losing the spelling says nothing about, so it is not checked. Putting a
// cross-vocabulary collision in Absent reports a spurious promotion.
Ignore map[string]string
}
Params configures a Check.