this repo has no description
at master 161 lines 4.7 kB view raw
1// Copyright 2018 The CUE Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package json 16 17import ( 18 "bytes" 19 "encoding/json" 20 "fmt" 21 "io" 22 "strings" 23 24 "cuelang.org/go/cue" 25 "cuelang.org/go/cue/ast" 26 "cuelang.org/go/cue/errors" 27 "cuelang.org/go/cue/parser" 28 "cuelang.org/go/cue/token" 29 cuejson "cuelang.org/go/encoding/json" 30 "cuelang.org/go/internal/core/adt" 31 internaljson "cuelang.org/go/internal/encoding/json" 32 "cuelang.org/go/internal/pkg" 33 "cuelang.org/go/internal/value" 34) 35 36// Compact generates the JSON-encoded src with insignificant space characters 37// elided. 38func Compact(src []byte) (string, error) { 39 dst := bytes.Buffer{} 40 if err := json.Compact(&dst, src); err != nil { 41 return "", err 42 } 43 return dst.String(), nil 44} 45 46// Indent creates an indented form of the JSON-encoded src. 47// Each element in a JSON object or array begins on a new, 48// indented line beginning with prefix followed by one or more 49// copies of indent according to the indentation nesting. 50// The data appended to dst does not begin with the prefix nor 51// any indentation, to make it easier to embed inside other formatted JSON data. 52// Although leading space characters (space, tab, carriage return, newline) 53// at the beginning of src are dropped, trailing space characters 54// at the end of src are preserved and copied to dst. 55// For example, if src has no trailing spaces, neither will dst; 56// if src ends in a trailing newline, so will dst. 57func Indent(src []byte, prefix, indent string) (string, error) { 58 dst := bytes.Buffer{} 59 if err := json.Indent(&dst, src, prefix, indent); err != nil { 60 return "", err 61 } 62 return dst.String(), nil 63} 64 65// HTMLEscape returns the JSON-encoded src with <, >, &, U+2028 and 66// U+2029 characters inside string literals changed to \u003c, \u003e, \u0026, 67// \u2028, \u2029 so that the JSON will be safe to embed inside HTML <script> 68// tags. For historical reasons, web browsers don't honor standard HTML escaping 69// within <script> tags, so an alternative JSON encoding must be used. 70func HTMLEscape(src []byte) string { 71 dst := &bytes.Buffer{} 72 json.HTMLEscape(dst, src) 73 return dst.String() 74} 75 76// Marshal returns the JSON encoding of v. 77func Marshal(v cue.Value) (string, error) { 78 b, err := internaljson.Marshal(v) 79 return string(b), err 80} 81 82// MarshalStream turns a list into a stream of JSON objects. 83func MarshalStream(v cue.Value) (string, error) { 84 // TODO: return an io.Reader and allow asynchronous processing. 85 iter, err := v.List() 86 if err != nil { 87 return "", err 88 } 89 var b strings.Builder 90 for iter.Next() { 91 p, err := internaljson.Marshal(iter.Value()) 92 if err != nil { 93 return "", err 94 } 95 b.Write(p) 96 b.WriteByte('\n') 97 } 98 return b.String(), nil 99} 100 101// UnmarshalStream parses the JSON to a CUE instance. 102func UnmarshalStream(data []byte) (ast.Expr, error) { 103 d := cuejson.NewDecoder(nil, "", bytes.NewReader(data)) 104 105 a := []ast.Expr{} 106 for { 107 x, err := d.Extract() 108 if err == io.EOF { 109 break 110 } 111 if err != nil { 112 return nil, err 113 } 114 a = append(a, x) 115 } 116 117 return ast.NewList(a...), nil 118} 119 120// Unmarshal parses the JSON-encoded data. 121func Unmarshal(b []byte) (ast.Expr, error) { 122 if !json.Valid(b) { 123 return nil, fmt.Errorf("json: invalid JSON") 124 } 125 expr, err := parser.ParseExpr("json", b) 126 if err != nil { 127 // NOTE: should never happen. 128 return nil, errors.Wrapf(err, token.NoPos, "json: could not parse JSON") 129 } 130 return expr, nil 131} 132 133// Validate validates JSON and confirms it matches the constraints 134// specified by v. 135func Validate(b []byte, v pkg.Schema) (bool, error) { 136 c := value.OpContext(v) 137 return validate(c, b, v) 138} 139 140// validate is the actual implementation of Validate. 141func validate(c *adt.OpContext, b []byte, v pkg.Schema) (bool, error) { 142 if !json.Valid(b) { 143 return false, fmt.Errorf("json: invalid JSON") 144 } 145 v2 := v.Context().CompileBytes(b, cue.Filename("json.Validate")) 146 if err := v2.Err(); err != nil { 147 return false, err 148 } 149 150 vx := adt.Unify(c, value.Vertex(v2), value.Vertex(v)) 151 v = value.Make(c, vx) 152 if err := v.Err(); err != nil { 153 return false, err 154 } 155 156 if err := v.Validate(cue.Final()); err != nil { 157 return false, err 158 } 159 160 return true, nil 161}