github.com/solo-io/cue@v0.4.7/encoding/protobuf/textproto/decoder.go (about)

     1  // Copyright 2021 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 textproto
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/solo-io/cue/cue"
    22  	"github.com/solo-io/cue/cue/ast"
    23  	"github.com/solo-io/cue/cue/errors"
    24  	"github.com/solo-io/cue/cue/literal"
    25  	"github.com/solo-io/cue/cue/token"
    26  	"github.com/solo-io/cue/encoding/protobuf/pbinternal"
    27  	"github.com/solo-io/cue/internal/core/adt"
    28  	"github.com/solo-io/cue/internal/value"
    29  
    30  	pbast "github.com/protocolbuffers/txtpbfmt/ast"
    31  	"github.com/protocolbuffers/txtpbfmt/parser"
    32  	"github.com/protocolbuffers/txtpbfmt/unquote"
    33  )
    34  
    35  // Option defines options for the decoder.
    36  // There are currently no options.
    37  type Option func(*options)
    38  
    39  type options struct {
    40  }
    41  
    42  // NewDecoder returns a new Decoder
    43  func NewDecoder(option ...Option) *Decoder {
    44  	d := &Decoder{}
    45  	_ = d.m // work around linter bug.
    46  	return d
    47  }
    48  
    49  // A Decoder caches conversions of cue.Value between calls to its methods.
    50  type Decoder struct {
    51  	m map[*adt.Vertex]*mapping
    52  }
    53  
    54  type decoder struct {
    55  	*Decoder
    56  
    57  	// Reset on each call
    58  	errs errors.Error
    59  	file *token.File
    60  }
    61  
    62  // Parse parses the given textproto bytes and converts them to a CUE expression,
    63  // using schema as the guideline for conversion using the following rules:
    64  //
    65  //    - the @protobuf attribute is optional, but is necessary for:
    66  //        - interpreting protobuf maps
    67  //        - using a name different from the CUE name
    68  //    - fields in the textproto that have no corresponding field in
    69  //      schema are ignored
    70  //
    71  // NOTE: the filename is used for associating position information. However,
    72  // currently no position information is associated with the text proto because
    73  // the position information of github.com/protocolbuffers/txtpbfmt is too
    74  // unreliable to be useful.
    75  func (d *Decoder) Parse(schema cue.Value, filename string, b []byte) (ast.Expr, error) {
    76  	dec := decoder{Decoder: d}
    77  
    78  	// dec.errs = nil
    79  
    80  	f := token.NewFile(filename, 0, len(b))
    81  	f.SetLinesForContent(b)
    82  	dec.file = f
    83  
    84  	cfg := parser.Config{}
    85  	nodes, err := parser.ParseWithConfig(b, cfg)
    86  	if err != nil {
    87  		return nil, errors.Newf(token.NoPos, "textproto: %v", err)
    88  	}
    89  
    90  	m := dec.parseSchema(schema)
    91  	if dec.errs != nil {
    92  		return nil, dec.errs
    93  	}
    94  
    95  	n := dec.decodeMsg(m, nodes)
    96  	if dec.errs != nil {
    97  		return nil, dec.errs
    98  	}
    99  
   100  	return n, nil
   101  }
   102  
   103  // Don't expose until the protobuf APIs settle down.
   104  // func (d *decoder) Decode(schema cue.Value, textpbfmt) (cue.Value, error) {
   105  // }
   106  
   107  type mapping struct {
   108  	children map[string]*fieldInfo
   109  }
   110  
   111  type fieldInfo struct {
   112  	pbinternal.Info
   113  	msg *mapping
   114  	// keytype, for now
   115  }
   116  
   117  func (d *decoder) addErr(err error) {
   118  	d.errs = errors.Append(d.errs, errors.Promote(err, "textproto"))
   119  }
   120  
   121  func (d *decoder) addErrf(pos pbast.Position, format string, args ...interface{}) {
   122  	err := errors.Newf(d.protoPos(pos), "textproto: "+format, args...)
   123  	d.errs = errors.Append(d.errs, err)
   124  }
   125  
   126  func (d *decoder) protoPos(p pbast.Position) token.Pos {
   127  	return d.file.Pos(int(p.Byte), token.NoRelPos)
   128  }
   129  
   130  // parseSchema walks over a CUE "type", converts it to an internal data
   131  // structure that is used for parsing text proto, and writes it to
   132  //
   133  func (d *decoder) parseSchema(schema cue.Value) *mapping {
   134  	_, v := value.ToInternal(schema)
   135  	if v == nil {
   136  		return nil
   137  	}
   138  
   139  	if d.m == nil {
   140  		d.m = map[*adt.Vertex]*mapping{}
   141  	} else if m := d.m[v]; m != nil {
   142  		return m
   143  	}
   144  
   145  	m := &mapping{children: map[string]*fieldInfo{}}
   146  
   147  	i, err := schema.Fields()
   148  	if err != nil {
   149  		d.addErr(err)
   150  		return nil
   151  	}
   152  
   153  	for i.Next() {
   154  		info, err := pbinternal.FromIter(i)
   155  		if err != nil {
   156  			d.addErr(err)
   157  			continue
   158  		}
   159  
   160  		var msg *mapping
   161  
   162  		switch info.CompositeType {
   163  		case pbinternal.Normal:
   164  			switch info.ValueType {
   165  			case pbinternal.Message:
   166  				msg = d.parseSchema(i.Value())
   167  			}
   168  
   169  		case pbinternal.List, pbinternal.Map:
   170  			e, _ := i.Value().Elem()
   171  			if e.IncompleteKind() == cue.StructKind {
   172  				msg = d.parseSchema(e)
   173  			}
   174  		}
   175  
   176  		m.children[info.Name] = &fieldInfo{
   177  			Info: info,
   178  			msg:  msg,
   179  		}
   180  	}
   181  
   182  	d.m[v] = m
   183  	return m
   184  }
   185  
   186  func (d *decoder) decodeMsg(m *mapping, n []*pbast.Node) ast.Expr {
   187  	st := &ast.StructLit{}
   188  
   189  	var listMap map[string]*ast.ListLit
   190  
   191  	for _, x := range n {
   192  		if x.Values == nil && x.Children == nil {
   193  			if cg := addComments(x.PreComments...); cg != nil {
   194  				ast.SetRelPos(cg, token.NewSection)
   195  				st.Elts = append(st.Elts, cg)
   196  				continue
   197  			}
   198  		}
   199  		if m == nil {
   200  			continue
   201  		}
   202  		f, ok := m.children[x.Name]
   203  		if !ok {
   204  			continue // ignore unknown fields
   205  		}
   206  
   207  		var value ast.Expr
   208  
   209  		switch f.CompositeType {
   210  		default:
   211  			value = d.decodeValue(f, x)
   212  
   213  		case pbinternal.List:
   214  			if listMap == nil {
   215  				listMap = make(map[string]*ast.ListLit)
   216  			}
   217  
   218  			list := listMap[f.CUEName]
   219  			if list == nil {
   220  				list = &ast.ListLit{}
   221  				listMap[f.CUEName] = list
   222  				value = list
   223  			}
   224  
   225  			if len(x.Values) == 1 || f.ValueType == pbinternal.Message {
   226  				v := d.decodeValue(f, x)
   227  				if value == nil {
   228  					if cg := addComments(x.PreComments...); cg != nil {
   229  						cg.Doc = true
   230  						ast.AddComment(v, cg)
   231  					}
   232  				}
   233  				if cg := addComments(x.PostValuesComments...); cg != nil {
   234  					cg.Position = 4
   235  					ast.AddComment(v, cg)
   236  				}
   237  				list.Elts = append(list.Elts, v)
   238  				break
   239  			}
   240  
   241  			var last ast.Expr
   242  			// Handle [1, 2, 3]
   243  			for _, v := range x.Values {
   244  				if v.Value == "" {
   245  					if cg := addComments(v.PreComments...); cg != nil {
   246  						if last != nil {
   247  							cg.Position = 4
   248  							ast.AddComment(last, cg)
   249  						} else {
   250  							cg.Position = 1
   251  							ast.AddComment(list, cg)
   252  						}
   253  					}
   254  					continue
   255  				}
   256  				y := *x
   257  				y.Values = []*pbast.Value{v}
   258  				last = d.decodeValue(f, &y)
   259  				list.Elts = append(list.Elts, last)
   260  			}
   261  			if cg := addComments(x.PostValuesComments...); cg != nil {
   262  				if last != nil {
   263  					cg.Position = 4
   264  					ast.AddComment(last, cg)
   265  				} else {
   266  					cg.Position = 1
   267  					ast.AddComment(list, cg)
   268  				}
   269  			}
   270  			if cg := addComments(x.ClosingBraceComment); cg != nil {
   271  				cg.Position = 4
   272  				ast.AddComment(list, cg)
   273  			}
   274  
   275  		case pbinternal.Map:
   276  			// mapValue: {
   277  			//     key: 123
   278  			//     value: "string"
   279  			// }
   280  			if k := len(x.Values); k > 0 {
   281  				d.addErrf(x.Start, "values not allowed for Message type; found %d", k)
   282  			}
   283  
   284  			var (
   285  				key ast.Label
   286  				val ast.Expr
   287  			)
   288  
   289  			for _, c := range x.Children {
   290  				if len(c.Values) != 1 {
   291  					d.addErrf(x.Start, "expected 1 value, found %d", len(c.Values))
   292  					continue
   293  				}
   294  				s := c.Values[0].Value
   295  
   296  				switch c.Name {
   297  				case "key":
   298  					if strings.HasPrefix(s, `"`) {
   299  						key = &ast.BasicLit{Kind: token.STRING, Value: s}
   300  					} else {
   301  						key = ast.NewString(s)
   302  					}
   303  
   304  				case "value":
   305  					val = d.decodeValue(f, c)
   306  
   307  					if cg := addComments(x.ClosingBraceComment); cg != nil {
   308  						cg.Line = true
   309  						ast.AddComment(val, cg)
   310  					}
   311  
   312  				default:
   313  					d.addErrf(c.Start, "unsupported key name %q in map", c.Name)
   314  					continue
   315  				}
   316  			}
   317  
   318  			if key != nil && val != nil {
   319  				value = ast.NewStruct(key, val)
   320  			}
   321  		}
   322  
   323  		if value != nil {
   324  			var label ast.Label
   325  			if s := f.CUEName; ast.IsValidIdent(s) {
   326  				label = ast.NewIdent(s)
   327  			} else {
   328  				label = ast.NewString(s)
   329  
   330  			}
   331  			// TODO: convert line number information. However, position
   332  			// information in textpbfmt packages is too wonky to be useful
   333  			f := &ast.Field{
   334  				Label: label,
   335  				Value: value,
   336  				// Attrs: []*ast.Attribute{{Text: f.attr.}},
   337  			}
   338  			if cg := addComments(x.PreComments...); cg != nil {
   339  				cg.Doc = true
   340  				ast.AddComment(f, cg)
   341  			}
   342  			st.Elts = append(st.Elts, f)
   343  		}
   344  	}
   345  
   346  	return st
   347  }
   348  
   349  func addComments(lines ...string) (cg *ast.CommentGroup) {
   350  	var a []*ast.Comment
   351  	for _, c := range lines {
   352  		if !strings.HasPrefix(c, "#") {
   353  			continue
   354  		}
   355  		a = append(a, &ast.Comment{Text: "//" + c[1:]})
   356  	}
   357  	if a != nil {
   358  		cg = &ast.CommentGroup{List: a}
   359  	}
   360  	return cg
   361  }
   362  
   363  func (d *decoder) decodeValue(f *fieldInfo, n *pbast.Node) (x ast.Expr) {
   364  	if f.ValueType == pbinternal.Message {
   365  		if k := len(n.Values); k > 0 {
   366  			d.addErrf(n.Start, "values not allowed for Message type; found %d", k)
   367  		}
   368  		x = d.decodeMsg(f.msg, n.Children)
   369  		if cg := addComments(n.ClosingBraceComment); cg != nil {
   370  			cg.Line = true
   371  			cg.Position = 4
   372  			ast.AddComment(x, cg)
   373  		}
   374  		return x
   375  	}
   376  
   377  	if len(n.Values) != 1 {
   378  		d.addErrf(n.Start, "expected 1 value, found %d", len(n.Values))
   379  		return nil
   380  	}
   381  	v := n.Values[0]
   382  
   383  	defer func() {
   384  		if cg := addComments(v.PreComments...); cg != nil {
   385  			cg.Doc = true
   386  			ast.AddComment(x, cg)
   387  		}
   388  		if cg := addComments(v.InlineComment); cg != nil {
   389  			cg.Line = true
   390  			cg.Position = 2
   391  			ast.AddComment(x, cg)
   392  		}
   393  	}()
   394  
   395  	switch f.ValueType {
   396  	case pbinternal.String, pbinternal.Bytes:
   397  		s, err := unquote.Unquote(n)
   398  		if err != nil {
   399  			d.addErrf(n.Start, "invalid string or bytes: %v", err)
   400  		}
   401  		if f.ValueType == pbinternal.String {
   402  			s = literal.String.Quote(s)
   403  		} else {
   404  			s = literal.Bytes.Quote(s)
   405  		}
   406  		return &ast.BasicLit{Kind: token.STRING, Value: s}
   407  
   408  	case pbinternal.Bool:
   409  		switch v.Value {
   410  		case "true":
   411  			return ast.NewBool(true)
   412  
   413  		case "false":
   414  		default:
   415  			d.addErrf(n.Start, "invalid bool %s", v.Value)
   416  		}
   417  		return ast.NewBool(false)
   418  
   419  	case pbinternal.Int, pbinternal.Float:
   420  		s := v.Value
   421  		switch s {
   422  		case "inf", "nan":
   423  			// TODO: include message.
   424  			return &ast.BottomLit{}
   425  		}
   426  
   427  		var info literal.NumInfo
   428  		if err := literal.ParseNum(s, &info); err != nil {
   429  			var x ast.BasicLit
   430  			if pbinternal.MatchBySymbol(f.Value, s, &x) {
   431  				return &x
   432  			}
   433  			d.addErrf(n.Start, "invalid number %s", s)
   434  		}
   435  		if !info.IsInt() {
   436  			return &ast.BasicLit{Kind: token.FLOAT, Value: s}
   437  		}
   438  		return &ast.BasicLit{Kind: token.INT, Value: info.String()}
   439  
   440  	default:
   441  		panic(fmt.Sprintf("unexpected type %v", f.ValueType))
   442  	}
   443  }