mirror of
https://github.com/google/go-jsonnet.git
synced 2025-08-06 14:27:30 +02:00
150 lines
3.4 KiB
Go
150 lines
3.4 KiB
Go
/*
|
|
Copyright 2019 Google Inc. All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package jsonnet
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"io"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"sigs.k8s.io/yaml"
|
|
)
|
|
|
|
const separator = "---"
|
|
|
|
// YAMLToJSONDecoder decodes YAML documents from an io.Reader by
|
|
// separating individual documents. It first converts the YAML
|
|
// body to JSON, then unmarshals the JSON.
|
|
type YAMLToJSONDecoder struct {
|
|
reader *YAMLReader
|
|
}
|
|
|
|
// NewYAMLToJSONDecoder decodes YAML documents from the provided
|
|
// stream in chunks by converting each document (as defined by
|
|
// the YAML spec) into its own chunk, converting it to JSON via
|
|
// yaml.YAMLToJSON, and then passing it to json.Decoder.
|
|
func NewYAMLToJSONDecoder(r io.Reader) *YAMLToJSONDecoder {
|
|
reader := bufio.NewReader(r)
|
|
return &YAMLToJSONDecoder{
|
|
reader: NewYAMLReader(reader),
|
|
}
|
|
}
|
|
|
|
// Decode reads a YAML document as JSON from the stream or returns
|
|
// an error. The decoding rules match json.Unmarshal, not
|
|
// yaml.Unmarshal.
|
|
func (d *YAMLToJSONDecoder) Decode(into interface{}) error {
|
|
bytes, err := d.reader.read()
|
|
if err != nil && err != io.EOF {
|
|
return err
|
|
}
|
|
|
|
if len(bytes) != 0 {
|
|
err := yaml.Unmarshal(bytes, into)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (d *YAMLToJSONDecoder) IsStream() bool {
|
|
return d.reader.isStream()
|
|
}
|
|
|
|
// Reader reads bytes
|
|
type Reader interface {
|
|
Read() ([]byte, error)
|
|
}
|
|
|
|
// YAMLReader reads YAML
|
|
type YAMLReader struct {
|
|
reader Reader
|
|
stream bool
|
|
}
|
|
|
|
// NewYAMLReader creates a new YAMLReader
|
|
func NewYAMLReader(r *bufio.Reader) *YAMLReader {
|
|
return &YAMLReader{
|
|
reader: &LineReader{reader: r},
|
|
}
|
|
}
|
|
|
|
// Read returns a full YAML document.
|
|
func (r *YAMLReader) read() ([]byte, error) {
|
|
var buffer bytes.Buffer
|
|
for {
|
|
line, err := r.reader.Read()
|
|
if err != nil && err != io.EOF {
|
|
return nil, err
|
|
}
|
|
|
|
sep := len([]byte(separator))
|
|
if i := bytes.Index(line, []byte(separator)); i == 0 {
|
|
// We have a potential document terminator
|
|
i += sep
|
|
after := line[i:]
|
|
if len(strings.TrimRightFunc(string(after), unicode.IsSpace)) == 0 {
|
|
r.stream = true
|
|
if buffer.Len() != 0 {
|
|
return buffer.Bytes(), nil
|
|
}
|
|
if err == io.EOF {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
if err == io.EOF {
|
|
if buffer.Len() != 0 {
|
|
// If we're at EOF, we have a final, non-terminated line. Return it.
|
|
return buffer.Bytes(), nil
|
|
}
|
|
return nil, err
|
|
}
|
|
buffer.Write(line)
|
|
}
|
|
}
|
|
|
|
func (r *YAMLReader) isStream() bool {
|
|
return r.stream
|
|
}
|
|
|
|
// LineReader reads single lines.
|
|
type LineReader struct {
|
|
reader *bufio.Reader
|
|
}
|
|
|
|
// Read returns a single line (with '\n' ended) from the underlying reader.
|
|
// An error is returned iff there is an error with the underlying reader.
|
|
func (r *LineReader) Read() ([]byte, error) {
|
|
var (
|
|
isPrefix bool = true
|
|
err error = nil
|
|
line []byte
|
|
buffer bytes.Buffer
|
|
)
|
|
|
|
for isPrefix && err == nil {
|
|
line, isPrefix, err = r.reader.ReadLine()
|
|
buffer.Write(line)
|
|
}
|
|
buffer.WriteByte('\n')
|
|
return buffer.Bytes(), err
|
|
}
|