1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package json
17
18 import (
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/ast/astutil"
27 "cuelang.org/go/cue/errors"
28 "cuelang.org/go/cue/literal"
29 "cuelang.org/go/cue/parser"
30 "cuelang.org/go/cue/token"
31 "cuelang.org/go/internal/value"
32 )
33
34
35 func Valid(b []byte) bool {
36 return json.Valid(b)
37 }
38
39
40
41 func Validate(b []byte, v cue.Value) error {
42 if !json.Valid(b) {
43 return fmt.Errorf("json: invalid JSON")
44 }
45 r := value.ConvertToRuntime(v.Context())
46 inst, err := r.Compile("json.Validate", b)
47 if err != nil {
48 return err
49 }
50
51 v = v.Unify(inst.Value())
52 if v.Err() != nil {
53 return v.Err()
54 }
55 return v.Validate(cue.Final())
56 }
57
58
59
60 func Extract(path string, data []byte) (ast.Expr, error) {
61 expr, err := extract(path, data)
62 if err != nil {
63 return nil, err
64 }
65 patchExpr(expr)
66 return expr, nil
67 }
68
69
70
71
72
73 func Decode(r *cue.Runtime, path string, data []byte) (*cue.Instance, error) {
74 expr, err := extract(path, data)
75 if err != nil {
76 return nil, err
77 }
78 return r.CompileExpr(expr)
79 }
80
81 func extract(path string, b []byte) (ast.Expr, error) {
82 expr, err := parser.ParseExpr(path, b)
83 if err != nil || !json.Valid(b) {
84 p := token.NoPos
85 if pos := errors.Positions(err); len(pos) > 0 {
86 p = pos[0]
87 }
88 var x interface{}
89 err := json.Unmarshal(b, &x)
90 return nil, errors.Wrapf(err, p, "invalid JSON for file %q", path)
91 }
92 return expr, nil
93 }
94
95
96
97
98
99
100 func NewDecoder(r *cue.Runtime, path string, src io.Reader) *Decoder {
101 return &Decoder{
102 r: r,
103 path: path,
104 dec: json.NewDecoder(src),
105 }
106 }
107
108
109 type Decoder struct {
110 r *cue.Runtime
111 path string
112 dec *json.Decoder
113 }
114
115
116
117 func (d *Decoder) Extract() (ast.Expr, error) {
118 expr, err := d.extract()
119 if err != nil {
120 return expr, err
121 }
122 patchExpr(expr)
123 return expr, nil
124 }
125
126 func (d *Decoder) extract() (ast.Expr, error) {
127 var raw json.RawMessage
128 err := d.dec.Decode(&raw)
129 if err == io.EOF {
130 return nil, err
131 }
132 if err != nil {
133 pos := token.NewFile(d.path, -1, len(raw)).Pos(0, 0)
134 return nil, errors.Wrapf(err, pos, "invalid JSON for file %q", d.path)
135 }
136 expr, err := parser.ParseExpr(d.path, []byte(raw))
137
138 if err != nil {
139 return nil, err
140 }
141 return expr, nil
142 }
143
144
145
146
147
148 func (d *Decoder) Decode() (*cue.Instance, error) {
149 expr, err := d.Extract()
150 if err != nil {
151 return nil, err
152 }
153 return d.r.CompileExpr(expr)
154 }
155
156
157
158
159
160 func patchExpr(n ast.Node) {
161 type info struct {
162 reflow bool
163 }
164 stack := []info{{true}}
165
166 afterFn := func(n ast.Node) {
167 switch n.(type) {
168 case *ast.ListLit, *ast.StructLit:
169 stack = stack[:len(stack)-1]
170 }
171 }
172
173 var beforeFn func(n ast.Node) bool
174
175 beforeFn = func(n ast.Node) bool {
176 isLarge := n.End().Offset()-n.Pos().Offset() > 50
177 descent := true
178
179 switch x := n.(type) {
180 case *ast.ListLit:
181 reflow := true
182 if !isLarge {
183 for _, e := range x.Elts {
184 if hasSpaces(e) {
185 reflow = false
186 break
187 }
188 }
189 }
190 stack = append(stack, info{reflow})
191 if reflow {
192 x.Lbrack = x.Lbrack.WithRel(token.NoRelPos)
193 x.Rbrack = x.Rbrack.WithRel(token.NoRelPos)
194 }
195 return true
196
197 case *ast.StructLit:
198 reflow := true
199 if !isLarge {
200 for _, e := range x.Elts {
201 if f, ok := e.(*ast.Field); !ok || hasSpaces(f) || hasSpaces(f.Value) {
202 reflow = false
203 break
204 }
205 }
206 }
207 stack = append(stack, info{reflow})
208 if reflow {
209 x.Lbrace = x.Lbrace.WithRel(token.NoRelPos)
210 x.Rbrace = x.Rbrace.WithRel(token.NoRelPos)
211 }
212 return true
213
214 case *ast.Field:
215
216 switch {
217 case true:
218 s, ok := x.Label.(*ast.BasicLit)
219 if !ok || s.Kind != token.STRING {
220 break
221 }
222
223 u, err := literal.Unquote(s.Value)
224 if err != nil {
225 break
226 }
227
228
229
230 if !ast.IsValidIdent(u) || strings.HasPrefix(u, "_") {
231 break
232 }
233
234 x.Label = ast.NewIdent(u)
235 astutil.CopyMeta(x.Label, s)
236 }
237 ast.Walk(x.Value, beforeFn, afterFn)
238 descent = false
239
240 case *ast.BasicLit:
241 if x.Kind == token.STRING && len(x.Value) > 10 {
242 s, err := literal.Unquote(x.Value)
243 if err != nil {
244 break
245 }
246
247 x.Value = literal.String.WithOptionalTabIndent(len(stack)).Quote(s)
248 }
249 }
250
251 if stack[len(stack)-1].reflow {
252 ast.SetRelPos(n, token.NoRelPos)
253 }
254 return descent
255 }
256
257 ast.Walk(n, beforeFn, afterFn)
258 }
259
260 func hasSpaces(n ast.Node) bool {
261 return n.Pos().RelPos() > token.NoSpace
262 }
263
View as plain text