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  }