Add Example tests and fix raw value extraction for boolean types
Add two godoc Example tests: - ExampleDecoder_EnableUnmarshalerInterface_dynamicConfig: shows dynamic unmarshaling based on a type field - ExampleDecoder_EnableUnmarshalerInterface_rawMessage: demonstrates RawMessage usage for deferred parsing Fix handleKeyValuesUnmarshaler to handle values where Raw.Length == 0 (like boolean types) by using value.Data as fallback.
This commit is contained in:
+9
-2
@@ -729,8 +729,15 @@ func (d *decoder) handleKeyValuesUnmarshaler(u unstable.Unmarshaler) (reflect.Va
|
||||
// Get the raw value bytes
|
||||
value := expr.Value()
|
||||
if value != nil {
|
||||
raw := d.p.Raw(value.Raw)
|
||||
buf = append(buf, raw...)
|
||||
if value.Raw.Length > 0 {
|
||||
// Use raw bytes from the original document
|
||||
raw := d.p.Raw(value.Raw)
|
||||
buf = append(buf, raw...)
|
||||
} else {
|
||||
// Some value types (like Bool) don't have Raw set,
|
||||
// use Data which contains the value representation
|
||||
buf = append(buf, value.Data...)
|
||||
}
|
||||
}
|
||||
buf = append(buf, '\n')
|
||||
}
|
||||
|
||||
@@ -96,6 +96,132 @@ func ExampleUnmarshal() {
|
||||
// tags: [go toml]
|
||||
}
|
||||
|
||||
// pluginConfig demonstrates how to implement dynamic unmarshaling
|
||||
// based on a "type" field. This pattern is useful for plugin systems
|
||||
// or polymorphic configuration.
|
||||
type pluginConfig struct {
|
||||
Type string
|
||||
Config any
|
||||
}
|
||||
|
||||
func (p *pluginConfig) UnmarshalTOML(data []byte) error {
|
||||
// First, decode just the type field
|
||||
var typeOnly struct {
|
||||
Type string `toml:"type"`
|
||||
}
|
||||
if err := toml.Unmarshal(data, &typeOnly); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Type = typeOnly.Type
|
||||
|
||||
// Now decode the config based on the type
|
||||
switch typeOnly.Type {
|
||||
case "database":
|
||||
var cfg struct {
|
||||
Type string `toml:"type"`
|
||||
Host string `toml:"host"`
|
||||
Port int `toml:"port"`
|
||||
}
|
||||
if err := toml.Unmarshal(data, &cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Config = map[string]any{"host": cfg.Host, "port": cfg.Port}
|
||||
case "cache":
|
||||
var cfg struct {
|
||||
Type string `toml:"type"`
|
||||
TTL int `toml:"ttl"`
|
||||
}
|
||||
if err := toml.Unmarshal(data, &cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Config = map[string]any{"ttl": cfg.TTL}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// This example demonstrates dynamic unmarshaling based on a discriminator
|
||||
// field. The pluginConfig type uses UnmarshalTOML to first read the "type"
|
||||
// field, then decode the rest of the configuration based on that type.
|
||||
// This pattern is useful for plugin systems or configuration that varies
|
||||
// by type.
|
||||
func ExampleDecoder_EnableUnmarshalerInterface_dynamicConfig() {
|
||||
doc := `
|
||||
[[plugins]]
|
||||
type = "database"
|
||||
host = "localhost"
|
||||
port = 5432
|
||||
|
||||
[[plugins]]
|
||||
type = "cache"
|
||||
ttl = 300
|
||||
`
|
||||
type Config struct {
|
||||
Plugins []pluginConfig `toml:"plugins"`
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
err := toml.NewDecoder(strings.NewReader(doc)).
|
||||
EnableUnmarshalerInterface().
|
||||
Decode(&cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, p := range cfg.Plugins {
|
||||
fmt.Printf("type=%s config=%v\n", p.Type, p.Config)
|
||||
}
|
||||
// Output:
|
||||
// type=database config=map[host:localhost port:5432]
|
||||
// type=cache config=map[ttl:300]
|
||||
}
|
||||
|
||||
// This example demonstrates using RawMessage to capture raw TOML bytes
|
||||
// for later processing. RawMessage is similar to json.RawMessage - it
|
||||
// delays decoding so you can inspect the raw content or decode it
|
||||
// differently based on context.
|
||||
func ExampleDecoder_EnableUnmarshalerInterface_rawMessage() {
|
||||
doc := `
|
||||
[plugin]
|
||||
name = "example"
|
||||
version = "1.0"
|
||||
enabled = true
|
||||
`
|
||||
|
||||
type Config struct {
|
||||
Plugin unstable.RawMessage `toml:"plugin"`
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
err := toml.NewDecoder(strings.NewReader(doc)).
|
||||
EnableUnmarshalerInterface().
|
||||
Decode(&cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// cfg.Plugin contains the raw TOML bytes
|
||||
fmt.Printf("Raw TOML captured:\n%s", cfg.Plugin)
|
||||
|
||||
// You can later decode it into a specific type
|
||||
var plugin struct {
|
||||
Name string `toml:"name"`
|
||||
Version string `toml:"version"`
|
||||
Enabled bool `toml:"enabled"`
|
||||
}
|
||||
if err := toml.Unmarshal(cfg.Plugin, &plugin); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("Decoded: name=%s version=%s enabled=%v\n",
|
||||
plugin.Name, plugin.Version, plugin.Enabled)
|
||||
|
||||
// Output:
|
||||
// Raw TOML captured:
|
||||
// name = "example"
|
||||
// version = "1.0"
|
||||
// enabled = true
|
||||
// Decoded: name=example version=1.0 enabled=true
|
||||
}
|
||||
|
||||
type badReader struct{}
|
||||
|
||||
func (r *badReader) Read([]byte) (int, error) {
|
||||
|
||||
Reference in New Issue
Block a user