Also change NewJSONStreamDecoder to use use types.MediaType as argument. The
MediaType type-alias that was added in 0e7c8176e8
to help discoverability of accepted media/content-types.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
63 lines
1.3 KiB
Go
63 lines
1.3 KiB
Go
package internal
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"slices"
|
|
|
|
"github.com/moby/moby/api/types"
|
|
)
|
|
|
|
const rs = 0x1E
|
|
|
|
type DecoderFn func(v any) error
|
|
|
|
// NewJSONStreamDecoder builds a DecoderFn to read a stream of JSON records
|
|
// formatted with the specified content-type.
|
|
func NewJSONStreamDecoder(r io.Reader, contentType types.MediaType) DecoderFn {
|
|
switch contentType {
|
|
case types.MediaTypeJSONSequence:
|
|
return json.NewDecoder(NewRSFilterReader(r)).Decode
|
|
case types.MediaTypeJSON, types.MediaTypeNDJSON, types.MediaTypeJSONLines:
|
|
fallthrough
|
|
default:
|
|
return json.NewDecoder(r).Decode
|
|
}
|
|
}
|
|
|
|
type rsFilterReader struct {
|
|
reader io.Reader
|
|
}
|
|
|
|
// NewRSFilterReader creates an [io.Reader] that filters out ASCII Record Separators (RS).
|
|
func NewRSFilterReader(r io.Reader) io.Reader {
|
|
return &rsFilterReader{reader: r}
|
|
}
|
|
|
|
func (r *rsFilterReader) Read(p []byte) (int, error) {
|
|
if len(p) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
for {
|
|
n, err := r.reader.Read(p)
|
|
if n == 0 {
|
|
return 0, err
|
|
}
|
|
|
|
filtered := slices.DeleteFunc(p[:n], func(b byte) bool { return b == rs })
|
|
n = len(filtered)
|
|
if err != nil {
|
|
if err == io.EOF && n > 0 {
|
|
return n, nil
|
|
}
|
|
return n, err
|
|
}
|
|
if n == 0 {
|
|
// Avoid returning (0, nil) after consuming input; keep reading until data or an error (e.g., EOF).
|
|
continue
|
|
}
|
|
return n, nil
|
|
}
|
|
}
|