This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+40 -51
View File
@@ -9,6 +9,7 @@ package json
import (
"errors"
"fmt"
"io"
"reflect"
"sync"
@@ -18,16 +19,7 @@ import (
"github.com/go-json-experiment/json/jsontext"
)
// SkipFunc may be returned by [MarshalToFunc] and [UnmarshalFromFunc] functions.
//
// Any function that returns SkipFunc must not cause observable side effects
// on the provided [jsontext.Encoder] or [jsontext.Decoder].
// For example, it is permissible to call [jsontext.Decoder.PeekKind],
// but not permissible to call [jsontext.Decoder.ReadToken] or
// [jsontext.Encoder.WriteToken] since such methods mutate the state.
var SkipFunc = errors.New("json: skip function")
var errSkipMutation = errors.New("must not read or write any tokens when skipping")
var errUnsupportedMutation = errors.New("unsupported calls must not read or write any tokens")
var errNonSingularValue = errors.New("must read or write exactly one value")
// Marshalers is a list of functions that may override the marshal behavior
@@ -40,7 +32,8 @@ type Marshalers = typedMarshalers
// JoinMarshalers constructs a flattened list of marshal functions.
// If multiple functions in the list are applicable for a value of a given type,
// then those earlier in the list take precedence over those that come later.
// If a function returns [SkipFunc], then the next applicable function is called,
// If a function returns [errors.ErrUnsupported],
// then the next applicable function is called,
// otherwise the default marshaling behavior is used.
//
// For example:
@@ -62,7 +55,8 @@ type Unmarshalers = typedUnmarshalers
// JoinUnmarshalers constructs a flattened list of unmarshal functions.
// If multiple functions in the list are applicable for a value of a given type,
// then those earlier in the list take precedence over those that come later.
// If a function returns [SkipFunc], then the next applicable function is called,
// If a function returns [errors.ErrUnsupported],
// then the next applicable function is called,
// otherwise the default unmarshaling behavior is used.
//
// For example:
@@ -150,7 +144,7 @@ func (a *typedArshalers[Coder]) lookup(fnc func(*Coder, addressableValue, *jsono
fncDefault := fnc
fnc = func(c *Coder, v addressableValue, o *jsonopts.Struct) error {
for _, fnc := range fncs {
if err := fnc(c, v, o); err != SkipFunc {
if err := fnc(c, v, o); !errors.Is(err, errors.ErrUnsupported) {
return err // may be nil or non-nil
}
}
@@ -168,18 +162,19 @@ func (a *typedArshalers[Coder]) lookup(fnc func(*Coder, addressableValue, *jsono
// The function is always provided with a non-nil pointer value
// if T is an interface or pointer type.
//
// The function must marshal exactly one JSON value.
// The value of T must not be retained outside the function call.
// It may not return [SkipFunc].
// Implementations must follow the requirements of [Marshaler].
//
// Implementations must not retain the value of T.
func MarshalFunc[T any](fn func(T) ([]byte, error)) *Marshalers {
t := reflect.TypeFor[T]()
assertCastableTo(t, true)
typFnc := typedMarshaler{
typ: t,
fnc: func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
val, err := fn(va.castTo(t).Interface().(T))
v, _ := reflect.TypeAssert[T](va.castTo(t))
val, err := fn(v)
if err != nil {
err = wrapSkipFunc(err, "marshal function of type func(T) ([]byte, error)")
err = wrapErrUnsupported(err, "marshal function of type func(T) ([]byte, error)")
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalFunc") // unlike unmarshal, always wrapped
}
@@ -207,12 +202,10 @@ func MarshalFunc[T any](fn func(T) ([]byte, error)) *Marshalers {
// The function is always provided with a non-nil pointer value
// if T is an interface or pointer type.
//
// The function must marshal exactly one JSON value by calling write methods
// on the provided encoder. It may return [SkipFunc] such that marshaling can
// move on to the next marshal function. However, no mutable method calls may
// be called on the encoder if [SkipFunc] is returned.
// The pointer to [jsontext.Encoder] and the value of T
// must not be retained outside the function call.
// Implementations must follow the requirements of [MarshalerTo].
//
// Implementations must not retain the pointer to [jsontext.Encoder] or the
// value of T.
func MarshalToFunc[T any](fn func(*jsontext.Encoder, T) error) *Marshalers {
t := reflect.TypeFor[T]()
assertCastableTo(t, true)
@@ -222,18 +215,19 @@ func MarshalToFunc[T any](fn func(*jsontext.Encoder, T) error) *Marshalers {
xe := export.Encoder(enc)
prevDepth, prevLength := xe.Tokens.DepthLength()
xe.Flags.Set(jsonflags.WithinArshalCall | 1)
err := fn(enc, va.castTo(t).Interface().(T))
v, _ := reflect.TypeAssert[T](va.castTo(t))
err := fn(enc, v)
xe.Flags.Set(jsonflags.WithinArshalCall | 0)
currDepth, currLength := xe.Tokens.DepthLength()
if err == nil && (prevDepth != currDepth || prevLength+1 != currLength) {
err = errNonSingularValue
}
if err != nil {
if err == SkipFunc {
if errors.Is(err, errors.ErrUnsupported) {
if prevDepth == currDepth && prevLength == currLength {
return SkipFunc
return err // forward [errors.ErrUnsupported]
}
err = errSkipMutation
err = errUnsupportedMutation
}
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalToFunc") // unlike unmarshal, always wrapped
@@ -255,10 +249,9 @@ func MarshalToFunc[T any](fn func(*jsontext.Encoder, T) error) *Marshalers {
// T must be an unnamed pointer or an interface type.
// The function is always provided with a non-nil pointer value.
//
// The function must unmarshal exactly one JSON value.
// The input []byte must not be mutated.
// The input []byte and value T must not be retained outside the function call.
// It may not return [SkipFunc].
// Implementations must follow the requirements of [Unmarshaler].
//
// Implementations must not retain the value of T.
func UnmarshalFunc[T any](fn func([]byte, T) error) *Unmarshalers {
t := reflect.TypeFor[T]()
assertCastableTo(t, false)
@@ -269,9 +262,10 @@ func UnmarshalFunc[T any](fn func([]byte, T) error) *Unmarshalers {
if err != nil {
return err // must be a syntactic or I/O error
}
err = fn(val, va.castTo(t).Interface().(T))
v, _ := reflect.TypeAssert[T](va.castTo(t))
err = fn(val, v)
if err != nil {
err = wrapSkipFunc(err, "unmarshal function of type func([]byte, T) error")
err = wrapErrUnsupported(err, "unmarshal function of type func([]byte, T) error")
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return err // unlike marshal, never wrapped
}
@@ -289,12 +283,10 @@ func UnmarshalFunc[T any](fn func([]byte, T) error) *Unmarshalers {
// T must be an unnamed pointer or an interface type.
// The function is always provided with a non-nil pointer value.
//
// The function must unmarshal exactly one JSON value by calling read methods
// on the provided decoder. It may return [SkipFunc] such that unmarshaling can
// move on to the next unmarshal function. However, no mutable method calls may
// be called on the decoder if [SkipFunc] is returned.
// The pointer to [jsontext.Decoder] and the value of T
// must not be retained outside the function call.
// Implementations must follow the requirements of [UnmarshalerFrom].
//
// Implementations must not retain the pointer to [jsontext.Decoder] or the
// value of T.
func UnmarshalFromFunc[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers {
t := reflect.TypeFor[T]()
assertCastableTo(t, false)
@@ -303,19 +295,23 @@ func UnmarshalFromFunc[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers
fnc: func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
xd := export.Decoder(dec)
prevDepth, prevLength := xd.Tokens.DepthLength()
if prevDepth == 1 && xd.AtEOF() {
return io.EOF // check EOF early to avoid fn reporting an EOF
}
xd.Flags.Set(jsonflags.WithinArshalCall | 1)
err := fn(dec, va.castTo(t).Interface().(T))
v, _ := reflect.TypeAssert[T](va.castTo(t))
err := fn(dec, v)
xd.Flags.Set(jsonflags.WithinArshalCall | 0)
currDepth, currLength := xd.Tokens.DepthLength()
if err == nil && (prevDepth != currDepth || prevLength+1 != currLength) {
err = errNonSingularValue
}
if err != nil {
if err == SkipFunc {
if errors.Is(err, errors.ErrUnsupported) {
if prevDepth == currDepth && prevLength == currLength {
return SkipFunc
return err // forward [errors.ErrUnsupported]
}
err = errSkipMutation
err = errUnsupportedMutation
}
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
if err2 := xd.SkipUntil(prevDepth, prevLength+1); err2 != nil {
@@ -423,10 +419,3 @@ func castableToFromAny(to reflect.Type) bool {
}
return false
}
func wrapSkipFunc(err error, what string) error {
if err == SkipFunc {
return errors.New(what + " cannot be skipped")
}
return err
}