cuelang.org/go@v0.10.1/internal/encoding/json/encode.go (about) 1 // Copyright 2020 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 "math/big" 21 "strings" 22 23 "cuelang.org/go/cue/ast" 24 "cuelang.org/go/cue/errors" 25 "cuelang.org/go/cue/literal" 26 "cuelang.org/go/cue/token" 27 "cuelang.org/go/internal" 28 "cuelang.org/go/internal/astinternal" 29 ) 30 31 // Marshal is a replacement for [json.Marshal] without HTML escaping. 32 func Marshal(v any) ([]byte, error) { 33 var buf bytes.Buffer 34 enc := json.NewEncoder(&buf) 35 enc.SetEscapeHTML(false) 36 if err := enc.Encode(v); err != nil { 37 return nil, err 38 } 39 p := buf.Bytes() 40 // Unlike json.Marshal, json.Encoder.Encode adds a trailing newline. 41 p = bytes.TrimSuffix(p, []byte("\n")) 42 return p, nil 43 } 44 45 // Encode converts a CUE AST to unescaped JSON. 46 // 47 // The given file must only contain values that can be directly supported by 48 // JSON: 49 // 50 // Type Restrictions 51 // BasicLit 52 // File no imports, aliases, or definitions 53 // StructLit no embeddings, aliases, or definitions 54 // List 55 // Field must be regular; label must be a BasicLit or Ident 56 // 57 // Comments and attributes are ignored. 58 func Encode(n ast.Node) (b []byte, err error) { 59 e := encoder{} 60 err = e.encode(n) 61 if err != nil { 62 return nil, err 63 } 64 return e.w.Bytes(), nil 65 } 66 67 type encoder struct { 68 w bytes.Buffer 69 tab []byte 70 indentsAtLevel []int 71 indenting bool 72 unIndenting int 73 } 74 75 func (e *encoder) writeIndent(b byte) { 76 if e.indenting { 77 e.indentsAtLevel[len(e.indentsAtLevel)-1]++ 78 } else { 79 e.indentsAtLevel = append(e.indentsAtLevel, 0) 80 } 81 e.indenting = true 82 _ = e.w.WriteByte(b) 83 } 84 85 func (e *encoder) writeUnindent(b byte, pos, def token.Pos) { 86 if e.unIndenting > 0 { 87 e.unIndenting-- 88 } else { 89 e.unIndenting = e.indentsAtLevel[len(e.indentsAtLevel)-1] 90 e.indentsAtLevel = e.indentsAtLevel[:len(e.indentsAtLevel)-1] 91 } 92 e.indenting = false 93 e.ws(pos, def.RelPos()) 94 _ = e.w.WriteByte(b) 95 } 96 97 func (e *encoder) writeString(s string) { 98 _, _ = e.w.WriteString(s) 99 e.indenting = false 100 } 101 102 func (e *encoder) writeByte(b byte) { 103 _ = e.w.WriteByte(b) 104 } 105 106 func (e *encoder) write(b []byte) { 107 _, _ = e.w.Write(b) 108 e.indenting = false 109 } 110 111 func (e *encoder) indent() { 112 for range e.indentsAtLevel { 113 e.write(e.tab) 114 } 115 } 116 117 func (e *encoder) ws(pos token.Pos, default_ token.RelPos) { 118 rel := pos.RelPos() 119 if pos == token.NoPos { 120 rel = default_ 121 } 122 switch rel { 123 case token.NoSpace: 124 case token.Blank: 125 e.writeByte(' ') 126 case token.Newline: 127 e.writeByte('\n') 128 e.indent() 129 case token.NewSection: 130 e.writeString("\n\n") 131 e.indent() 132 } 133 } 134 func (e *encoder) encode(n ast.Node) error { 135 if e.tab == nil { 136 e.tab = []byte(" ") 137 } 138 const defPos = token.NoSpace 139 switch x := n.(type) { 140 case *ast.BasicLit: 141 e.ws(x.Pos(), defPos) 142 return e.encodeScalar(x, true) 143 144 case *ast.ListLit: 145 e.ws(foldNewline(x.Pos()), token.NoRelPos) 146 if len(x.Elts) == 0 { 147 e.writeString("[]") 148 return nil 149 } 150 e.writeIndent('[') 151 for i, x := range x.Elts { 152 if i > 0 { 153 e.writeString(",") 154 } 155 if err := e.encode(x); err != nil { 156 return err 157 } 158 } 159 e.writeUnindent(']', x.Rbrack, compactNewline(x.Elts[0].Pos())) 160 return nil 161 162 case *ast.StructLit: 163 e.ws(foldNewline(n.Pos()), token.NoRelPos) 164 return e.encodeDecls(x.Elts, x.Rbrace) 165 166 case *ast.File: 167 return e.encodeDecls(x.Decls, token.NoPos) 168 169 case *ast.UnaryExpr: 170 e.ws(foldNewline(x.Pos()), defPos) 171 l, ok := x.X.(*ast.BasicLit) 172 if ok && x.Op == token.SUB && (l.Kind == token.INT || l.Kind == token.FLOAT) { 173 e.writeByte('-') 174 return e.encodeScalar(l, false) 175 } 176 } 177 return errors.Newf(n.Pos(), "json: unsupported node %s (%T)", astinternal.DebugStr(n), n) 178 } 179 180 func (e *encoder) encodeScalar(l *ast.BasicLit, allowMinus bool) error { 181 switch l.Kind { 182 case token.INT: 183 var x big.Int 184 return e.setNum(l, allowMinus, &x) 185 186 case token.FLOAT: 187 var x big.Float 188 return e.setNum(l, allowMinus, &x) 189 190 case token.TRUE: 191 e.writeString("true") 192 193 case token.FALSE: 194 e.writeString("false") 195 196 case token.NULL: 197 e.writeString("null") 198 199 case token.STRING: 200 str, err := literal.Unquote(l.Value) 201 if err != nil { 202 return err 203 } 204 b, err := Marshal(str) 205 if err != nil { 206 return err 207 } 208 e.write(b) 209 210 default: 211 return errors.Newf(l.Pos(), "unknown literal type %v", l.Kind) 212 } 213 return nil 214 } 215 216 func (e *encoder) setNum(l *ast.BasicLit, allowMinus bool, x interface{}) error { 217 if !allowMinus && strings.HasPrefix(l.Value, "-") { 218 return errors.Newf(l.Pos(), "double minus not allowed") 219 } 220 var ni literal.NumInfo 221 if err := literal.ParseNum(l.Value, &ni); err != nil { 222 return err 223 } 224 e.writeString(ni.String()) 225 return nil 226 } 227 228 // encodeDecls converts a sequence of declarations to a value. If it encounters 229 // an embedded value, it will return this expression. This is more relaxed for 230 // structs than is currently allowed for CUE, but the expectation is that this 231 // will be allowed at some point. The input would still be illegal CUE. 232 func (e *encoder) encodeDecls(decls []ast.Decl, endPos token.Pos) error { 233 var embed ast.Expr 234 var fields []*ast.Field 235 236 for _, d := range decls { 237 switch x := d.(type) { 238 default: 239 return errors.Newf(x.Pos(), "json: unsupported node %s (%T)", astinternal.DebugStr(x), x) 240 241 case *ast.Package: 242 if embed != nil || fields != nil { 243 return errors.Newf(x.Pos(), "invalid package clause") 244 } 245 continue 246 247 case *ast.Field: 248 if !internal.IsRegularField(x) { 249 return errors.Newf(x.TokenPos, "json: definition or hidden field not allowed") 250 } 251 if x.Optional != token.NoPos { 252 return errors.Newf(x.Optional, "json: optional fields not allowed") 253 } 254 fields = append(fields, x) 255 256 case *ast.EmbedDecl: 257 if embed != nil { 258 return errors.Newf(x.Pos(), "json: multiple embedded values") 259 } 260 embed = x.Expr 261 262 case *ast.CommentGroup: 263 } 264 } 265 266 if embed != nil { 267 if fields != nil { 268 return errors.Newf(embed.Pos(), "json: embedding mixed with fields") 269 } 270 return e.encode(embed) 271 } 272 273 if len(fields) == 0 { 274 e.writeString("{}") 275 return nil 276 } 277 278 e.writeIndent('{') 279 pos := compactNewline(fields[0].Pos()) 280 if endPos == token.NoPos && pos.RelPos() == token.Blank { 281 pos = token.NoPos 282 } 283 firstPos := pos 284 const defPos = token.NoRelPos 285 for i, x := range fields { 286 if i > 0 { 287 e.writeByte(',') 288 pos = x.Pos() 289 } 290 name, _, err := ast.LabelName(x.Label) 291 if err != nil { 292 return errors.Newf(x.Label.Pos(), "json: only literal labels allowed") 293 } 294 b, err := Marshal(name) 295 if err != nil { 296 return err 297 } 298 e.ws(pos, defPos) 299 e.write(b) 300 e.writeByte(':') 301 302 if err := e.encode(x.Value); err != nil { 303 return err 304 } 305 } 306 e.writeUnindent('}', endPos, firstPos) 307 return nil 308 } 309 310 func compactNewline(pos token.Pos) token.Pos { 311 if pos.RelPos() == token.NewSection { 312 pos = token.Newline.Pos() 313 } 314 return pos 315 } 316 317 func foldNewline(pos token.Pos) token.Pos { 318 if pos.RelPos() >= token.Newline { 319 pos = token.Blank.Pos() 320 } 321 return pos 322 }