blob: bd1fc069b34175faad028c6844c4f627ca5561e1 [file] [log] [blame]
// Copyright 2018 The CUE Authors
//
// 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 json
import (
"bytes"
"encoding/json"
"fmt"
"cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/parser"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal/value"
)
// Compact generates the JSON-encoded src with insignificant space characters
// elided.
func Compact(src []byte) (string, error) {
dst := bytes.Buffer{}
if err := json.Compact(&dst, src); err != nil {
return "", err
}
return dst.String(), nil
}
// Indent creates an indented form of the JSON-encoded src.
// Each element in a JSON object or array begins on a new,
// indented line beginning with prefix followed by one or more
// copies of indent according to the indentation nesting.
// The data appended to dst does not begin with the prefix nor
// any indentation, to make it easier to embed inside other formatted JSON data.
// Although leading space characters (space, tab, carriage return, newline)
// at the beginning of src are dropped, trailing space characters
// at the end of src are preserved and copied to dst.
// For example, if src has no trailing spaces, neither will dst;
// if src ends in a trailing newline, so will dst.
func Indent(src []byte, prefix, indent string) (string, error) {
dst := bytes.Buffer{}
if err := json.Indent(&dst, src, prefix, indent); err != nil {
return "", err
}
return dst.String(), nil
}
// HTMLEscape returns the JSON-encoded src with <, >, &, U+2028 and
// U+2029 characters inside string literals changed to \u003c, \u003e, \u0026,
// \u2028, \u2029 so that the JSON will be safe to embed inside HTML <script>
// tags. For historical reasons, web browsers don't honor standard HTML escaping
// within <script> tags, so an alternative JSON encoding must be used.
func HTMLEscape(src []byte) string {
dst := &bytes.Buffer{}
json.HTMLEscape(dst, src)
return dst.String()
}
// Marshal returns the JSON encoding of v.
func Marshal(v cue.Value) (string, error) {
b, err := json.Marshal(v)
return string(b), err
}
// MarshalStream turns a list into a stream of JSON objects.
func MarshalStream(v cue.Value) (string, error) {
// TODO: return an io.Reader and allow asynchronous processing.
iter, err := v.List()
if err != nil {
return "", err
}
buf := &bytes.Buffer{}
for iter.Next() {
b, err := json.Marshal(iter.Value())
if err != nil {
return "", err
}
buf.Write(b)
buf.WriteByte('\n')
}
return buf.String(), nil
}
// Unmarshal parses the JSON-encoded data.
func Unmarshal(b []byte) (ast.Expr, error) {
if !json.Valid(b) {
return nil, fmt.Errorf("json: invalid JSON")
}
expr, err := parser.ParseExpr("json", b)
if err != nil {
// NOTE: should never happen.
return nil, errors.Wrapf(err, token.NoPos, "json: could not parse JSON")
}
return expr, nil
}
// Validate validates JSON and confirms it matches the constraints
// specified by v.
func Validate(b []byte, v cue.Value) (bool, error) {
if !json.Valid(b) {
return false, fmt.Errorf("json: invalid JSON")
}
r := value.ConvertToRuntime(v.Context())
inst, err := r.Compile("json.Validate", b)
if err != nil {
return false, err
}
v = v.Unify(inst.Value())
if v.Err() != nil {
return false, v.Err()
}
return true, nil
}