Skip to content

Releases: wrfly/ecp

v0.3.1

Choose a tag to compare

@wrfly wrfly released this 19 Aug 16:46
3247af7

Bug fix release. Three fixes, no new features, no API change.

A separator you chose was silently replaced

Regression from v0.3.0. Making the default separator collapse repeats was implemented as "route the value through strings.Fields whenever the separator looks like whitespace", which threw away a separator you deliberately set:

e := ecp.New()
e.Advance.SplitChar = "\t"
// value "a b\tc d"
// v0.2.5: ["a b" "c d"]        correct
// v0.3.0: ["a" "b" "c" "d"]    wrong
// v0.3.1: ["a b" "c d"]        correct again

Only the default separator collapses repeats now, and it does so by dropping empty elements instead of re-splitting — so a tab inside an element is no longer treated as a separator either.

An optional section explicitly set to zero stayed nil

Pointer sections, new in v0.3.0, were allocated when the filled-in value came out non-zero. That cannot tell "nothing was said about this section" from "every value in it is zero":

P_PORT=0   ->   cfg.P == nil     // explicitly asked for, silently dropped

It also left List and Get disagreeing: List advertised P_PORT, and Get answered key not found for it.

The walk now tracks whether a field was actually assigned, from the environment or from a default tag, and allocates on that. This matches what a scalar pointer has always done — *int with default:"0" gives you &0, not nil.

A section nothing was said about still stays nil.

Errors blamed the wrong part of the value

Present in every earlier version. A value that merely contained an e was reported as a failure to parse the fragment after it:

value before after
not-a-number strconv.Atoi: parsing "r" strconv.ParseInt: parsing "not-a-number"
hello strconv.Atoi: parsing "llo" strconv.ParseInt: parsing "hello"

A non-numeric exponent means the value is not scientific notation at all, so it is reported whole. 1e, 1e1e1e and 1e-3 still error, and errors.As still unwraps to *strconv.NumError.

Upgrading

Drop-in from v0.3.0. The only behaviour that changes is the three bugs above.

v0.3.0

Choose a tag to compare

@wrfly wrfly released this 19 Aug 15:55
aed75d2

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.Duration

Values 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.

v0.2.5

Choose a tag to compare

@wrfly wrfly released this 15 Aug 19:13
d14d831

Bug-fix release (PR #23): edge-case values that previously produced silent wrong values, errors, or panics.

Fixes

  • int fields no longer accept duration syntax (e.g. 10s used to silently set the value to 1e10 nanoseconds); only time.Duration fields are parsed as durations
  • int/uint/float fields and pointers are parsed with their bit size, so out-of-range values error out instead of being silently truncated (e.g. int8 field with 1000 used to become -24)
  • []int64 with duration-like values and []time.Duration with plain numbers no longer panic in reflect.Value.Set
  • *[]T pointer-to-slice fields now parse correctly (previously failed with "field is not addressable")
  • environment values now overwrite non-nil pointer fields, matching the documented behavior
  • parseScientific rejects negative exponents (e.g. 1e-3 used to silently become 1) and handles comma+exponent combos like 1,000e3
  • Parse(&c) where c is already a pointer no longer panics; nil or non-struct configs return an error instead of panicking, List returns an empty list

Tests

  • All existing tests pass; 13 regression tests added in bugfix_test.go

v0.2.4

Choose a tag to compare

@wrfly wrfly released this 20 Nov 10:05
b741aea

fix: parse empty yaml/json fields

v0.2.3

Choose a tag to compare

@wrfly wrfly released this 16 Nov 03:41
0a39d47

fix: ignore unexported fields

v0.2.2

Choose a tag to compare

@wrfly wrfly released this 28 Aug 05:51
40f2106

bug fix

v0.2.1

Choose a tag to compare

@wrfly wrfly released this 27 Jul 10:04
94722b6

fix: parse time.Duration slice

v0.2.0

Choose a tag to compare

@wrfly wrfly released this 26 Jun 09:26
1d87c7d
  • refactored some functions and renamed some variables
  • implement SetValueFunc

v0.1.4

Choose a tag to compare

@wrfly wrfly released this 11 Jun 16:15

emmmm, several fixes

v0.1.3

Choose a tag to compare

@wrfly wrfly released this 07 Jan 02:35
73601f1
feat: obj-level functions (#13)

feat: obj-level functions