Repository navigation
v0.3.0
Panics
Values were built with a hard-coded concrete type and assigned to the field, which panics as soon as the field is not exactly that type — both reachable from an ordinary environment value:
type Level int
type Conf struct{ L []Level } // reflect.Set: []int is not assignable to []Level
type Conf struct{ D *time.Duration } // reflect.Set: *int64 is not assignable to *time.DurationValues are now built through the field's own reflect.Type, so named types behave like their underlying kind. *time.Duration also gains the "10s" syntax that time.Duration already had.
Fixes
| before | after | |
|---|---|---|
PORT=1e1000000 |
~100s building a 1MB string | rejected immediately |
1.5e3 |
unparsable "1.5000" |
1500 |
yaml:"-" / json:"-" / env:"-" |
honoured by List, ignored by Parse |
skipped by both |
Parse(config) without & |
nil error, nothing filled |
error |
*SubStruct section |
never walked into, bogus key in List |
optional section, allocated only when set, cycle-guarded |
default:"false" on a bool |
overwrote a caller-set true |
kept, like every other kind |
map / array fields |
silently ignored | reported, and no longer listed |
"a b" (default separator) |
3 elements, numeric slices fail | 2 elements |
List quoting |
A="a "b" c" |
strconv.Quote |
Get |
no prefix, pointer-only config, missed named types | prefix param, works read-only, kind-based |
| parse errors | %s |
%w, so errors.Is/As work |
| parser type | unexported *ecp |
exported *ECP |
Upgrading
Behaviour changes on purpose: Parse without a pointer now errors, --tagged fields are no longer filled, a caller-set bool survives default:"false", and unsupported kinds error instead of being ignored. The exported type and the new Get prefix argument are source-compatible.
Internals
One setValue helper replaces the fifteen near-identical branches of parseSlice and all of parsePointer (parse.go: 291 → 78 lines). Coverage 91.5% → 94.2%; CI now runs vet, -race and gofmt over Go 1.19/1.21/1.23/stable.