fix: decode untagged embedded structs and maps inline
Test / test (push) Successful in 1m26s

Assisted-by: GLM 5.3 Flash
This commit is contained in:
2026-09-17 20:05:50 +02:00
parent 93c36cf376
commit fc50e3c49a
4 changed files with 226 additions and 28 deletions
+14 -8
View File
@@ -100,16 +100,23 @@ For a struct destination, a TOML key matches a field as follows:
1. The `toml:"name"` tag, using the part before any comma. The literal `-`
excludes the field.
2. Without a tag, the lower-cased field name.
3. The key itself is lower-cased before lookup, so the match is
3. An anonymous (embedded) field without a tag is inlined: the decoder walks
into the embedded struct and matches its own fields against the same keys,
mirroring how the encoder flattens it. A nil embedded pointer struct is
allocated on demand. An untagged embedded map receives the keys no field
claims.
4. The key itself is lower-cased before lookup, so the match is
case-insensitive on both sides: `DATABASEURL` matches a field named
`DatabaseUrl`.
The match is exact after lower-casing. No separator is inserted, so a TOML key
`database_url` does not match a field named `DatabaseUrl`; tag such a field
(`toml:"database_url"`) or use the lower-cased name as the key. When two fields
resolve to the same name, the one declared later wins.
(`toml:"database_url"`) or use the lower-cased name as the key. When two
fields resolve to the same name, the shallower one wins; at equal depth, the
one declared later wins.
Unknown keys are ignored by default; see [Strict decoding](#strict-decoding).
Unknown keys are ignored by default, landing in an untagged embedded map when
the struct has one; [Strict decoding](#strict-decoding) rejects them instead.
### Numeric conversion
@@ -257,10 +264,9 @@ type Config struct {
Options combine after the name: `toml:"name,omitempty,omitzero"` is valid, and
an unknown option is ignored.
Note the asymmetry: the encoder inlines untagged embedded structs, while the
decoder expects them under their lower-cased type name. A struct with an
untagged embedded struct therefore does not round-trip through `Unmarshal` into
the same type.
Untagged embedded fields round-trip: the decoder inlines embedded structs and
routes unclaimed keys into an embedded map exactly where the encoder flattened
them.
### Group-by-kind layout