feat!: rework the public API to json/v2-style variadic options
Test / test (push) Successful in 1m49s
Test / test (push) Successful in 1m49s
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+169
-189
@@ -17,7 +17,7 @@
|
||||
// tree := doc.Map()
|
||||
//
|
||||
// A Decoder allows strict decoding that rejects keys without a matching
|
||||
// struct field, mirroring (*json.Decoder).DisallowUnknownFields.
|
||||
// struct field, mirroring the RejectUnknownFields option of encoding/json/v2.
|
||||
package interpres
|
||||
|
||||
import (
|
||||
@@ -259,6 +259,11 @@ func parseWithOptions(ctx context.Context, data []byte, opts parseOptions, wantD
|
||||
// Unmarshal parses a TOML document and stores the result in the value pointed
|
||||
// to by v. v is typically a pointer to a struct or to a map[string]any.
|
||||
//
|
||||
// Unmarshal parses a TOML document and stores the result in the value pointed
|
||||
// to by v. v is typically a pointer to a struct or to a map[string]any.
|
||||
// Options tune the call; with none, unknown keys are ignored, numbers are
|
||||
// evaluated, and the nesting default applies.
|
||||
//
|
||||
// Struct fields are matched to TOML keys by the `toml:"name"` tag, or by a
|
||||
// case-insensitive match on the field name when no tag is present. A tag of
|
||||
// "-" skips the field.
|
||||
@@ -269,8 +274,8 @@ func parseWithOptions(ctx context.Context, data []byte, opts parseOptions, wantD
|
||||
// bare integer as its nanosecond count.
|
||||
//
|
||||
// Unmarshal is equivalent to UnmarshalContext with context.Background.
|
||||
func Unmarshal(data []byte, v any) error {
|
||||
return UnmarshalContext(context.Background(), data, v)
|
||||
func Unmarshal(data []byte, v any, opts ...UnmarshalOption) error {
|
||||
return UnmarshalContext(context.Background(), data, v, opts...)
|
||||
}
|
||||
|
||||
// ParseAs decodes a TOML document into T in one call, the generic shorthand
|
||||
@@ -304,120 +309,76 @@ func NewSchema[T any]() {
|
||||
}
|
||||
|
||||
// UnmarshalContext is the cancellable variant of Unmarshal.
|
||||
func UnmarshalContext(ctx context.Context, data []byte, v any) error {
|
||||
dec := newDecoder()
|
||||
dec.ctx = ctx
|
||||
if canTargetDecode(v) {
|
||||
// The targeted parse fills struct destinations without the
|
||||
// intermediate tree; a document or destination it cannot model falls
|
||||
// back to the tree path, whose contracts it keeps.
|
||||
if err := parseIntoTargeted(ctx, data, dec, false, 0, v); err != errTargetFallback {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Only a destination that can reach an OrderedMap needs the node tree the
|
||||
// written key order is read from; every other decode skips building it.
|
||||
tree, doc, err := parseWithOptions(ctx, data, parseOptions{}, typeWantsOrder(reflect.TypeOf(v)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.nodes = indexNodes(doc.Root())
|
||||
return dec.decode(tree, v)
|
||||
func UnmarshalContext(ctx context.Context, data []byte, v any, opts ...UnmarshalOption) error {
|
||||
return settingsFor(opts).decode(ctx, data, v)
|
||||
}
|
||||
|
||||
// A Decoder decodes a TOML document into a Go value with configurable
|
||||
// strictness and configurable limits on the parse it performs.
|
||||
type Decoder struct {
|
||||
// UnmarshalRead reads the document from r and decodes it into v, the
|
||||
// streaming-shaped entry the json/v2 vocabulary uses. The reader is
|
||||
// consumed in full, because the parser scans its source in place; the
|
||||
// options and the behaviour are Unmarshal's.
|
||||
func UnmarshalRead(r io.Reader, v any, opts ...UnmarshalOption) error {
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("interpres: read: %w", err)
|
||||
}
|
||||
return Unmarshal(data, v, opts...)
|
||||
}
|
||||
|
||||
// An UnmarshalOption configures one Unmarshal, UnmarshalContext,
|
||||
// UnmarshalRead or ParseAs call. Options are function values over the
|
||||
// private decode settings, the shape encoding/json/v2 uses for its own
|
||||
// options, and compose by simple listing:
|
||||
//
|
||||
// err := interpres.Unmarshal(data, &cfg,
|
||||
// interpres.RejectUnknownFields(true),
|
||||
// interpres.NumbersAsLiterals(true))
|
||||
//
|
||||
// A destination that the direct skeleton cannot model falls back to the
|
||||
// tree path, so every option means the same thing on every document.
|
||||
type UnmarshalOption func(*decodeSettings)
|
||||
|
||||
// decodeSettings is the option carrier of one decode call.
|
||||
type decodeSettings struct {
|
||||
disallowUnknown bool
|
||||
useNumber bool
|
||||
maxDepth int
|
||||
maxInputSize int
|
||||
localLoc *time.Location
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewDecoder returns a Decoder.
|
||||
func NewDecoder() *Decoder { return &Decoder{} }
|
||||
|
||||
// DisallowUnknownFields causes Decode to return an error when the document
|
||||
// contains a key with no matching destination struct field.
|
||||
func (d *Decoder) DisallowUnknownFields() *Decoder {
|
||||
d.disallowUnknown = true
|
||||
return d
|
||||
func settingsFor(opts []UnmarshalOption) *decodeSettings {
|
||||
s := &decodeSettings{ctx: context.Background()}
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// UseNumber causes the numbers of the document to reach the value tree as a
|
||||
// Number carrying the literal the document wrote, so 0x1f, 1_000, +1.0 and
|
||||
// inf survive a round trip with their spelling intact. A destination of a
|
||||
// concrete numeric kind still takes the evaluated value; the literal is kept
|
||||
// only where a Number, or an any, receives it.
|
||||
func (d *Decoder) UseNumber() *Decoder {
|
||||
d.useNumber = true
|
||||
return d
|
||||
}
|
||||
|
||||
// LocalTimeLocation sets the zone a local date-time is placed in when it
|
||||
// decodes into a time.Time destination. Without the option a local date-time
|
||||
// fills only its own wrapper type (LocalDateTime, LocalDate, LocalTime),
|
||||
// whose embedded time.Time is UTC; with the option, a time.Time destination
|
||||
// takes the value too, carried in the location given. A nil location restores
|
||||
// the default.
|
||||
func (d *Decoder) LocalTimeLocation(loc *time.Location) *Decoder {
|
||||
d.localLoc = loc
|
||||
return d
|
||||
}
|
||||
|
||||
// MaxDepth bounds how deeply arrays and inline tables may nest in a document
|
||||
// this decoder accepts. The parser is a recursive descent, so a document that
|
||||
// nests without bound would exhaust the stack; one that nests deeper than the
|
||||
// limit is rejected with a SyntaxError naming it instead. Use 0 or any
|
||||
// negative value for the default of 10000, which no hand-written document
|
||||
// approaches.
|
||||
func (d *Decoder) MaxDepth(depth int) *Decoder {
|
||||
d.maxDepth = depth
|
||||
return d
|
||||
}
|
||||
|
||||
// MaxInputSize bounds the size of a document this decoder accepts, in bytes; a
|
||||
// larger one is rejected before parsing starts. Use 0 or any negative value for
|
||||
// no limit, which is the default: the caller already holds the bytes, so the
|
||||
// size is a policy the caller sets rather than a protection the library
|
||||
// imposes on its own. Parse and ParseContext take no limit beyond the nesting
|
||||
// default.
|
||||
func (d *Decoder) MaxInputSize(size int) *Decoder {
|
||||
d.maxInputSize = size
|
||||
return d
|
||||
}
|
||||
|
||||
// Decode parses data and stores the result in the value pointed to by v,
|
||||
// honouring the decoder's strictness settings.
|
||||
//
|
||||
// Decode is equivalent to DecodeContext with context.Background.
|
||||
func (d *Decoder) Decode(data []byte, v any) error {
|
||||
return d.DecodeContext(context.Background(), data, v)
|
||||
}
|
||||
|
||||
// DecodeContext is the cancellable variant of Decode.
|
||||
func (d *Decoder) DecodeContext(ctx context.Context, data []byte, v any) error {
|
||||
// decode runs the decode the settings describe: the targeted parse when the
|
||||
// destination takes it, the tree path otherwise or on fallback.
|
||||
func (s *decodeSettings) decode(ctx context.Context, data []byte, v any) error {
|
||||
dec := newDecoder()
|
||||
dec.disallowUnknown = d.disallowUnknown
|
||||
dec.disallowUnknown = s.disallowUnknown
|
||||
dec.ctx = ctx
|
||||
dec.loc = d.localLoc
|
||||
dec.loc = s.localLoc
|
||||
if canTargetDecode(v) {
|
||||
// The targeted parse fills struct destinations without the
|
||||
// intermediate tree; a document or destination it cannot model falls
|
||||
// back to the tree path, whose contracts it keeps. The size limit is
|
||||
// checked here, the targeted parse being the parse itself.
|
||||
if d.maxInputSize > 0 && len(data) > d.maxInputSize {
|
||||
return fmt.Errorf("interpres: input is %d bytes, over the limit of %d", len(data), d.maxInputSize)
|
||||
if s.maxInputSize > 0 && len(data) > s.maxInputSize {
|
||||
return fmt.Errorf("interpres: input is %d bytes, over the limit of %d", len(data), s.maxInputSize)
|
||||
}
|
||||
if err := parseIntoTargeted(ctx, data, dec, d.useNumber, d.maxDepth, v); err != errTargetFallback {
|
||||
if err := parseIntoTargeted(ctx, data, dec, s.useNumber, s.maxDepth, v); err != errTargetFallback {
|
||||
return err
|
||||
}
|
||||
}
|
||||
opts := parseOptions{
|
||||
maxDepth: d.maxDepth,
|
||||
maxInputSize: d.maxInputSize,
|
||||
useNumber: d.useNumber,
|
||||
maxDepth: s.maxDepth,
|
||||
maxInputSize: s.maxInputSize,
|
||||
useNumber: s.useNumber,
|
||||
}
|
||||
tree, doc, err := parseWithOptions(ctx, data, opts, typeWantsOrder(reflect.TypeOf(v)))
|
||||
if err != nil {
|
||||
@@ -427,33 +388,50 @@ func (d *Decoder) DecodeContext(ctx context.Context, data []byte, v any) error {
|
||||
return dec.decode(tree, v)
|
||||
}
|
||||
|
||||
// DecodeOptions gathers the options a one-shot decode call can set, the
|
||||
// struct-shaped alternative to building a Decoder for a single document. The
|
||||
// zero value decodes with the defaults: unknown keys ignored, numbers
|
||||
// evaluated, and no limit beyond the nesting default.
|
||||
type DecodeOptions struct {
|
||||
// DisallowUnknownFields rejects a key with no matching struct field.
|
||||
DisallowUnknownFields bool
|
||||
// UseNumber keeps the numbers of the document as Number literals.
|
||||
UseNumber bool
|
||||
// MaxDepth bounds how deeply arrays and inline tables may nest; 0 takes
|
||||
// the default of 10000.
|
||||
MaxDepth int
|
||||
// MaxInputSize bounds the document size in bytes; 0 takes no limit.
|
||||
MaxInputSize int
|
||||
// RejectUnknownFields makes the decode fail when the document contains a
|
||||
// key with no matching destination struct field. Off by default: unknown
|
||||
// keys are ignored.
|
||||
func RejectUnknownFields(v bool) UnmarshalOption {
|
||||
return func(s *decodeSettings) { s.disallowUnknown = v }
|
||||
}
|
||||
|
||||
// UnmarshalWithOptions decodes data into v with the options set, the one-shot
|
||||
// form of building a Decoder. See DecodeOptions for the fields and their
|
||||
// defaults.
|
||||
func UnmarshalWithOptions(data []byte, v any, opts DecodeOptions) error {
|
||||
dec := &Decoder{
|
||||
disallowUnknown: opts.DisallowUnknownFields,
|
||||
useNumber: opts.UseNumber,
|
||||
maxDepth: opts.MaxDepth,
|
||||
maxInputSize: opts.MaxInputSize,
|
||||
}
|
||||
return dec.DecodeContext(context.Background(), data, v)
|
||||
// NumbersAsLiterals keeps the numbers of the document as a Number carrying
|
||||
// the literal the document wrote, so 0x1f, 1_000, +1.0 and inf survive a
|
||||
// round trip with their spelling intact. A destination of a concrete numeric
|
||||
// kind still takes the evaluated value; the literal is kept only where a
|
||||
// Number, or an any, receives it. Off by default: numbers evaluate to
|
||||
// int64 and float64.
|
||||
func NumbersAsLiterals(v bool) UnmarshalOption {
|
||||
return func(s *decodeSettings) { s.useNumber = v }
|
||||
}
|
||||
|
||||
// LocalTimeLocation sets the zone a local date-time is placed in when it
|
||||
// decodes into a time.Time destination. Without the option a local date-time
|
||||
// fills only its own wrapper type (LocalDateTime, LocalDate, LocalTime),
|
||||
// whose embedded time.Time is UTC; with the option, a time.Time destination
|
||||
// takes the value too, carried in the location given. A nil location restores
|
||||
// the default.
|
||||
func LocalTimeLocation(loc *time.Location) UnmarshalOption {
|
||||
return func(s *decodeSettings) { s.localLoc = loc }
|
||||
}
|
||||
|
||||
// MaxNestingDepth bounds how deeply arrays and inline tables may nest in a
|
||||
// document the decode accepts. The parser is a recursive descent, so a
|
||||
// document that nests without bound would exhaust the stack; one that nests
|
||||
// deeper than the limit is rejected with a SyntaxError naming it instead.
|
||||
// Use 0 or any negative value for the default of 10000, which no
|
||||
// hand-written document approaches.
|
||||
func MaxNestingDepth(depth int) UnmarshalOption {
|
||||
return func(s *decodeSettings) { s.maxDepth = depth }
|
||||
}
|
||||
|
||||
// MaxInputSize bounds the size of a document the decode accepts, in bytes; a
|
||||
// larger one is rejected before parsing starts. Use 0 or any negative value
|
||||
// for no limit, which is the default: the caller already holds the bytes, so
|
||||
// the size is a policy the caller sets rather than a protection the library
|
||||
// imposes on its own.
|
||||
func MaxInputSize(size int) UnmarshalOption {
|
||||
return func(s *decodeSettings) { s.maxInputSize = size }
|
||||
}
|
||||
|
||||
// Marshaler is the interface implemented by types that can produce a custom
|
||||
@@ -543,9 +521,13 @@ type UnmarshalerContext interface {
|
||||
// string quoting style, and the choice between `[table]` headers and inline
|
||||
// tables are not preserved.
|
||||
//
|
||||
// Marshal returns the TOML encoding of v. Options tune the emission; with
|
||||
// none, the layout groups entries by kind, empty arrays emit and sub-tables
|
||||
// take the header form.
|
||||
//
|
||||
// Marshal is equivalent to MarshalContext with context.Background.
|
||||
func Marshal(v any) ([]byte, error) {
|
||||
return MarshalContext(context.Background(), v)
|
||||
func Marshal(v any, opts ...MarshalOption) ([]byte, error) {
|
||||
return MarshalContext(context.Background(), v, opts...)
|
||||
}
|
||||
|
||||
// A Statement is one top-level statement of a document, what Statements
|
||||
@@ -612,10 +594,10 @@ func Statements(r io.Reader) iter.Seq2[Statement, error] {
|
||||
}
|
||||
|
||||
// MarshalAppend appends the TOML encoding of v to buf and returns the extended
|
||||
// buffer, the shape json.MarshalAppend has. A failed encoding leaves buf
|
||||
// untouched and comes back with a nil slice.
|
||||
func MarshalAppend(buf []byte, v any) ([]byte, error) {
|
||||
out, err := Marshal(v)
|
||||
// buffer, the shape json/v2's MarshalAppendTo and json's MarshalAppend have.
|
||||
// A failed encoding leaves buf untouched and comes back with a nil slice.
|
||||
func MarshalAppend(buf []byte, v any, opts ...MarshalOption) ([]byte, error) {
|
||||
out, err := Marshal(v, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -623,11 +605,25 @@ func MarshalAppend(buf []byte, v any) ([]byte, error) {
|
||||
}
|
||||
|
||||
// MarshalContext is the cancellable variant of Marshal.
|
||||
func MarshalContext(ctx context.Context, v any) ([]byte, error) {
|
||||
func MarshalContext(ctx context.Context, v any, opts ...MarshalOption) ([]byte, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewEncoder().MarshalContext(ctx, v)
|
||||
return settingsForEncode(opts).marshal(ctx, v)
|
||||
}
|
||||
|
||||
// MarshalWrite encodes v and writes the document to w, the streaming-shaped
|
||||
// entry the json/v2 vocabulary uses. The options and the behaviour are
|
||||
// Marshal's.
|
||||
func MarshalWrite(w io.Writer, v any, opts ...MarshalOption) error {
|
||||
out, err := Marshal(v, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(out); err != nil {
|
||||
return fmt.Errorf("interpres: write: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A LayoutKind names the layout the encoder writes a document's entries in.
|
||||
@@ -642,48 +638,58 @@ const (
|
||||
LayoutKindDeclaration
|
||||
)
|
||||
|
||||
// An Encoder encodes Go values into TOML.
|
||||
// A MarshalOption configures one Marshal, MarshalContext, MarshalAppend or
|
||||
// MarshalWrite call. Options are function values over the private encode
|
||||
// settings, the shape encoding/json/v2 uses for its own, and compose by
|
||||
// simple listing:
|
||||
//
|
||||
// All options default to the behaviour that passes the toml-test compliance
|
||||
// suite in both directions:
|
||||
//
|
||||
// Layout: LayoutKindGrouped (scalars first, then tables, then
|
||||
// arrays of tables)
|
||||
// OmitEmptyArrays: false (a nil/empty []string slice emits [] as a value;
|
||||
// a nil/empty []Item struct slice is still skipped)
|
||||
// LiteralMultilineAt: 0 (always emit the escaped basic form, never a
|
||||
// literal one)
|
||||
// InlineTablesAt: 0 (always emit a table header, never an inline
|
||||
// table)
|
||||
//
|
||||
// Use the chainable option methods to opt out. The option state is private;
|
||||
// callers that need the underlying knobs reach for the methods rather than
|
||||
// reading or mutating fields.
|
||||
type Encoder struct {
|
||||
layout LayoutKind // default LayoutKindGrouped; set via (*Encoder).Layout
|
||||
omitEmptyArrays bool // default false; set via (*Encoder).OmitEmptyArrays
|
||||
literalMultilineAt int // default 0; set via (*Encoder).LiteralMultiline
|
||||
inlineTablesAt int // default 0; set via (*Encoder).InlineTables
|
||||
emitFieldComments bool // default false; set via (*Encoder).EmitFieldComments
|
||||
// out, err := interpres.Marshal(cfg,
|
||||
// interpres.Layout(interpres.LayoutKindDeclaration),
|
||||
// interpres.InlineTables(60))
|
||||
type MarshalOption func(*encodeSettings)
|
||||
|
||||
// encodeSettings is the option carrier of one encode call.
|
||||
type encodeSettings struct {
|
||||
ctx context.Context
|
||||
cfg encodeConfig
|
||||
}
|
||||
|
||||
// NewEncoder returns an Encoder with default options.
|
||||
func NewEncoder() *Encoder { return &Encoder{layout: LayoutKindGrouped} }
|
||||
func settingsForEncode(opts []MarshalOption) *encodeSettings {
|
||||
s := &encodeSettings{ctx: context.Background(), cfg: encodeConfig{layout: LayoutKindGrouped}}
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// marshal runs the encode the settings describe.
|
||||
func (s *encodeSettings) marshal(ctx context.Context, v any) ([]byte, error) {
|
||||
enc := newEncoder()
|
||||
enc.ctx = ctx
|
||||
enc.opts = s.cfg
|
||||
if err := enc.encode(v); err != nil {
|
||||
enc.release()
|
||||
return nil, err
|
||||
}
|
||||
// The output leaves the pooled buffer as a copy, so the next Marshal
|
||||
// reuses the buffer without touching what the caller holds.
|
||||
out := slices.Clone(enc.buf.Bytes())
|
||||
enc.release()
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Layout sets the layout the encoder writes a document's entries in:
|
||||
// LayoutKindGrouped, the default, reorders them scalars first, then tables,
|
||||
// then arrays of tables; LayoutKindDeclaration preserves declaration order.
|
||||
func (e *Encoder) Layout(kind LayoutKind) *Encoder {
|
||||
e.layout = kind
|
||||
return e
|
||||
func Layout(kind LayoutKind) MarshalOption {
|
||||
return func(s *encodeSettings) { s.cfg.layout = kind }
|
||||
}
|
||||
|
||||
// OmitEmptyArrays opts in to skipping empty (non-nil, length 0) TOML arrays
|
||||
// of scalars. The default emits them as "key = []". Nil slices and empty
|
||||
// arrays of tables are already always omitted.
|
||||
func (e *Encoder) OmitEmptyArrays() *Encoder {
|
||||
e.omitEmptyArrays = true
|
||||
return e
|
||||
func OmitEmptyArrays(v bool) MarshalOption {
|
||||
return func(s *encodeSettings) { s.cfg.omitEmptyArrays = v }
|
||||
}
|
||||
|
||||
// LiteralMultiline sets the length threshold at which a multi-line string
|
||||
@@ -691,9 +697,8 @@ func (e *Encoder) OmitEmptyArrays() *Encoder {
|
||||
// Use 0 or any negative value to disable (always escaped). The literal form
|
||||
// is selected only when the value contains an internal newline; otherwise the
|
||||
// single-line basic form is used regardless of this setting.
|
||||
func (e *Encoder) LiteralMultiline(threshold int) *Encoder {
|
||||
e.literalMultilineAt = threshold
|
||||
return e
|
||||
func LiteralMultiline(threshold int) MarshalOption {
|
||||
return func(s *encodeSettings) { s.cfg.literalMultilineAt = threshold }
|
||||
}
|
||||
|
||||
// InlineTables sets the size limit, in bytes of the single-line rendering, at
|
||||
@@ -709,9 +714,8 @@ func (e *Encoder) LiteralMultiline(threshold int) *Encoder {
|
||||
// With LayoutKindDeclaration the layout is already for presentation only, and an
|
||||
// inlined table follows the same rule as any other value line: it lands in the
|
||||
// section of the header that precedes it.
|
||||
func (e *Encoder) InlineTables(threshold int) *Encoder {
|
||||
e.inlineTablesAt = threshold
|
||||
return e
|
||||
func InlineTables(threshold int) MarshalOption {
|
||||
return func(s *encodeSettings) { s.cfg.inlineTablesAt = threshold }
|
||||
}
|
||||
|
||||
// EmitFieldComments turns on printing the comment a field's `toml` tag
|
||||
@@ -724,30 +728,6 @@ func (e *Encoder) InlineTables(threshold int) *Encoder {
|
||||
// that carries the text. Off by default, and a field without a `comment=`
|
||||
// option prints none. Multi-line comments carry newlines in the tag, each
|
||||
// line printed with its own "# " marker.
|
||||
func (e *Encoder) EmitFieldComments() *Encoder {
|
||||
e.emitFieldComments = true
|
||||
return e
|
||||
}
|
||||
|
||||
// Marshal encodes v to TOML bytes. It is equivalent to calling Marshal with v.
|
||||
//
|
||||
// Marshal is equivalent to MarshalContext with context.Background.
|
||||
func (e *Encoder) Marshal(v any) ([]byte, error) {
|
||||
return e.MarshalContext(context.Background(), v)
|
||||
}
|
||||
|
||||
// MarshalContext is the cancellable variant of Marshal.
|
||||
func (e *Encoder) MarshalContext(ctx context.Context, v any) ([]byte, error) {
|
||||
enc := newEncoder()
|
||||
enc.ctx = ctx
|
||||
enc.opts = *e
|
||||
if err := enc.encode(v); err != nil {
|
||||
enc.release()
|
||||
return nil, err
|
||||
}
|
||||
// The output leaves the pooled buffer as a copy, so the next Marshal
|
||||
// reuses the buffer without touching what the caller holds.
|
||||
out := slices.Clone(enc.buf.Bytes())
|
||||
enc.release()
|
||||
return out, nil
|
||||
func EmitFieldComments(v bool) MarshalOption {
|
||||
return func(s *encodeSettings) { s.cfg.emitFieldComments = v }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user