Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+40
-51
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user