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