Marcel van Lohuizen | c24a519 | 2018-12-11 11:50:44 +0100 | [diff] [blame] | 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 | |
| 15 | package json |
| 16 | |
| 17 | import ( |
| 18 | "bytes" |
| 19 | "encoding/json" |
| 20 | "fmt" |
| 21 | |
| 22 | "cuelang.org/go/cue" |
| 23 | "cuelang.org/go/cue/parser" |
| 24 | "cuelang.org/go/cue/token" |
| 25 | ) |
| 26 | |
| 27 | // Compact generates the JSON-encoded src with insignificant space characters |
| 28 | // elided. |
| 29 | func Compact(src []byte) (string, error) { |
| 30 | dst := bytes.Buffer{} |
| 31 | if err := json.Compact(&dst, src); err != nil { |
| 32 | return "", err |
| 33 | } |
| 34 | return dst.String(), nil |
| 35 | } |
| 36 | |
| 37 | // Indent creates an indented form of the JSON-encoded src. |
| 38 | // Each element in a JSON object or array begins on a new, |
| 39 | // indented line beginning with prefix followed by one or more |
| 40 | // copies of indent according to the indentation nesting. |
| 41 | // The data appended to dst does not begin with the prefix nor |
| 42 | // any indentation, to make it easier to embed inside other formatted JSON data. |
| 43 | // Although leading space characters (space, tab, carriage return, newline) |
| 44 | // at the beginning of src are dropped, trailing space characters |
| 45 | // at the end of src are preserved and copied to dst. |
| 46 | // For example, if src has no trailing spaces, neither will dst; |
| 47 | // if src ends in a trailing newline, so will dst. |
| 48 | func Indent(src []byte, prefix, indent string) (string, error) { |
| 49 | dst := bytes.Buffer{} |
| 50 | if err := json.Indent(&dst, src, prefix, indent); err != nil { |
| 51 | return "", err |
| 52 | } |
| 53 | return dst.String(), nil |
| 54 | } |
| 55 | |
| 56 | // HTMLEscape returns the JSON-encoded src with <, >, &, U+2028 and |
| 57 | // U+2029 characters inside string literals changed to \u003c, \u003e, \u0026, |
| 58 | // \u2028, \u2029 so that the JSON will be safe to embed inside HTML <script> |
| 59 | // tags. For historical reasons, web browsers don't honor standard HTML escaping |
| 60 | // within <script> tags, so an alternative JSON encoding must be used. |
| 61 | func HTMLEscape(src []byte) string { |
| 62 | dst := &bytes.Buffer{} |
| 63 | json.HTMLEscape(dst, src) |
| 64 | return dst.String() |
| 65 | } |
| 66 | |
| 67 | // Marshal returns the JSON encoding of v. |
| 68 | func Marshal(v cue.Value) (string, error) { |
| 69 | b, err := json.Marshal(v) |
| 70 | return string(b), err |
| 71 | } |
| 72 | |
| 73 | // MarshalStream turns a list into a stream of JSON objects. |
| 74 | func MarshalStream(v cue.Value) (string, error) { |
| 75 | // TODO: return an io.Reader and allow asynchronous processing. |
| 76 | iter, err := v.List() |
| 77 | if err != nil { |
| 78 | return "", err |
| 79 | } |
| 80 | buf := &bytes.Buffer{} |
| 81 | for iter.Next() { |
| 82 | b, err := json.Marshal(iter.Value()) |
| 83 | if err != nil { |
| 84 | return "", err |
| 85 | } |
| 86 | buf.Write(b) |
| 87 | buf.WriteByte('\n') |
| 88 | } |
| 89 | return buf.String(), nil |
| 90 | } |
| 91 | |
| 92 | // Unmarshal parses the JSON-encoded data. |
| 93 | func Unmarshal(b []byte) (*cue.Instance, error) { |
| 94 | if !json.Valid(b) { |
| 95 | return nil, fmt.Errorf("json: invalid JSON") |
| 96 | } |
| 97 | fset := token.NewFileSet() |
| 98 | expr, err := parser.ParseExpr(fset, "json", b) |
| 99 | if err != nil { |
| 100 | // NOTE: should never happen. |
| 101 | return nil, fmt.Errorf("json: could not parse JSON: %v", err) |
| 102 | } |
| 103 | return cue.FromExpr(fset, expr) |
| 104 | } |