Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -46,6 +46,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
(10000 levels, which no hand-written document approaches): a document that
|
(10000 levels, which no hand-written document approaches): a document that
|
||||||
nests arrays or inline tables deeper used to run the stack out and is now
|
nests arrays or inline tables deeper used to run the stack out and is now
|
||||||
rejected with a `SyntaxError` naming the limit.
|
rejected with a `SyntaxError` naming the limit.
|
||||||
|
- `ParseAs[T](data)`, the generic one-line decode, and `NewSchema[T]()`,
|
||||||
|
which precompiles the struct schema and the interface flags for a hot path
|
||||||
|
before the first document arrives.
|
||||||
- `OrderedMap`, the string-keyed table that remembers the order its keys were
|
- `OrderedMap`, the string-keyed table that remembers the order its keys were
|
||||||
set in: decoding into one fills it in the order the document wrote the
|
set in: decoding into one fills it in the order the document wrote the
|
||||||
keys, and `Marshal` writes one back in that order, where a map carries no
|
keys, and `Marshal` writes one back in that order, where a map carries no
|
||||||
|
|||||||
+17
@@ -80,6 +80,23 @@ The one-shot form of a configured `Decoder`: the same options as
|
|||||||
The zero value takes the defaults: unknown keys ignored, numbers evaluated
|
The zero value takes the defaults: unknown keys ignored, numbers evaluated
|
||||||
as `int64` and `float64`, no size limit and the 10000-level nesting default.
|
as `int64` and `float64`, no size limit and the 10000-level nesting default.
|
||||||
|
|
||||||
|
### `func ParseAs[T any](data []byte) (T, error)`
|
||||||
|
|
||||||
|
The generic shorthand for `Unmarshal` with a destination variable:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg, err := interpres.ParseAs[Config](data)
|
||||||
|
```
|
||||||
|
|
||||||
|
The zero `T` comes back with the error.
|
||||||
|
|
||||||
|
### `func NewSchema[T any]()`
|
||||||
|
|
||||||
|
Precompiles the codec for `T`: the struct schema both directions walk and the
|
||||||
|
interface flags the decoder and encoder resolve through are built once and
|
||||||
|
cached, so the first document pays the cost instead of the hot path. A `T`
|
||||||
|
that is not a struct warms nothing.
|
||||||
|
|
||||||
## Documents
|
## Documents
|
||||||
|
|
||||||
`Parse` returns a `Document`: the value tree together with what a map cannot
|
`Parse` returns a `Document`: the value tree together with what a map cannot
|
||||||
|
|||||||
@@ -270,6 +270,36 @@ func Unmarshal(data []byte, v any) error {
|
|||||||
return UnmarshalContext(context.Background(), data, v)
|
return UnmarshalContext(context.Background(), data, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseAs decodes a TOML document into T in one call, the generic shorthand
|
||||||
|
// for Unmarshal with a destination variable:
|
||||||
|
//
|
||||||
|
// cfg, err := interpres.ParseAs[Config](data)
|
||||||
|
//
|
||||||
|
// The zero T comes back with the error.
|
||||||
|
func ParseAs[T any](data []byte) (T, error) {
|
||||||
|
var v T
|
||||||
|
err := Unmarshal(data, &v)
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSchema precompiles the codec for T: the struct schema both directions
|
||||||
|
// walk and the interface flags the decoder and the encoder resolve through
|
||||||
|
// are built once and cached, so the first document pays the cost instead of
|
||||||
|
// the hot path. A T that is not a struct warms nothing; there is nothing to
|
||||||
|
// precompute for a map or a slice.
|
||||||
|
func NewSchema[T any]() {
|
||||||
|
t := reflect.TypeFor[T]()
|
||||||
|
if t.Kind() != reflect.Struct {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cachedStructSchema(t)
|
||||||
|
_ = typeFlags(t)
|
||||||
|
_ = encTypeFlags(t)
|
||||||
|
pt := reflect.PointerTo(t)
|
||||||
|
_ = typeFlags(pt)
|
||||||
|
_ = encTypeFlags(pt)
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalContext is the cancellable variant of Unmarshal.
|
// UnmarshalContext is the cancellable variant of Unmarshal.
|
||||||
func UnmarshalContext(ctx context.Context, data []byte, v any) error {
|
func UnmarshalContext(ctx context.Context, data []byte, v any) error {
|
||||||
// Only a destination that can reach an OrderedMap needs the node tree the
|
// Only a destination that can reach an OrderedMap needs the node tree the
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -773,3 +774,25 @@ func TestValid(t *testing.T) {
|
|||||||
t.Errorf("Valid(invalid) = %v, want a SyntaxError", err)
|
t.Errorf("Valid(invalid) = %v, want a SyntaxError", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseAsAndNewSchema(t *testing.T) {
|
||||||
|
type Config struct {
|
||||||
|
Host string `toml:"host"`
|
||||||
|
Port int `toml:"port"`
|
||||||
|
}
|
||||||
|
cfg, err := ParseAs[Config]([]byte("host = \"db\"\nport = 5432\n"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if cfg.Host != "db" || cfg.Port != 5432 {
|
||||||
|
t.Errorf("decoded %+v", cfg)
|
||||||
|
}
|
||||||
|
if _, err := ParseAs[Config]([]byte("port =\n")); err == nil {
|
||||||
|
t.Error("ParseAs(invalid) succeeded, want an error and the zero value")
|
||||||
|
}
|
||||||
|
NewSchema[Config]()
|
||||||
|
if _, ok := structSchemaCache.Load(reflect.TypeFor[Config]()); !ok {
|
||||||
|
t.Error("NewSchema left no schema in the cache")
|
||||||
|
}
|
||||||
|
NewSchema[map[string]any]() // must not panic
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user