...

Source file src/cuelang.org/go/encoding/protobuf/textproto/decoder.go

Documentation: cuelang.org/go/encoding/protobuf/textproto

     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  	"cuelang.org/go/cue"
    22  	"cuelang.org/go/cue/ast"
    23  	"cuelang.org/go/cue/errors"
    24  	"cuelang.org/go/cue/literal"
    25  	"cuelang.org/go/cue/token"
    26  	"cuelang.org/go/encoding/protobuf/pbinternal"
    27  	"cuelang.org/go/internal/core/adt"
    28  	"cuelang.org/go/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, -1, 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  func (d *decoder) parseSchema(schema cue.Value) *mapping {
   133  	_, v := value.ToInternal(schema)
   134  	if v == nil {
   135  		return nil
   136  	}
   137  
   138  	if d.m == nil {
   139  		d.m = map[*adt.Vertex]*mapping{}
   140  	} else if m := d.m[v]; m != nil {
   141  		return m
   142  	}
   143  
   144  	m := &mapping{children: map[string]*fieldInfo{}}
   145  
   146  	i, err := schema.Fields()
   147  	if err != nil {
   148  		d.addErr(err)
   149  		return nil
   150  	}
   151  
   152  	for i.Next() {
   153  		info, err := pbinternal.FromIter(i)
   154  		if err != nil {
   155  			d.addErr(err)
   156  			continue
   157  		}
   158  
   159  		var msg *mapping
   160  
   161  		switch info.CompositeType {
   162  		case pbinternal.Normal:
   163  			switch info.ValueType {
   164  			case pbinternal.Message:
   165  				msg = d.parseSchema(i.Value())
   166  			}
   167  
   168  		case pbinternal.List, pbinternal.Map:
   169  			e, _ := i.Value().Elem()
   170  			if e.IncompleteKind() == cue.StructKind {
   171  				msg = d.parseSchema(e)
   172  			}
   173  		}
   174  
   175  		m.children[info.Name] = &fieldInfo{
   176  			Info: info,
   177  			msg:  msg,
   178  		}
   179  	}
   180  
   181  	d.m[v] = m
   182  	return m
   183  }
   184  
   185  func (d *decoder) decodeMsg(m *mapping, n []*pbast.Node) ast.Expr {
   186  	st := &ast.StructLit{}
   187  
   188  	var listMap map[string]*ast.ListLit
   189  
   190  	for _, x := range n {
   191  		if x.Values == nil && x.Children == nil {
   192  			if cg := addComments(x.PreComments...); cg != nil {
   193  				ast.SetRelPos(cg, token.NewSection)
   194  				st.Elts = append(st.Elts, cg)
   195  				continue
   196  			}
   197  		}
   198  		if m == nil {
   199  			continue
   200  		}
   201  		f, ok := m.children[x.Name]
   202  		if !ok {
   203  			continue // ignore unknown fields
   204  		}
   205  
   206  		var value ast.Expr
   207  
   208  		switch f.CompositeType {
   209  		default:
   210  			value = d.decodeValue(f, x)
   211  
   212  		case pbinternal.List:
   213  			if listMap == nil {
   214  				listMap = make(map[string]*ast.ListLit)
   215  			}
   216  
   217  			list := listMap[f.CUEName]
   218  			if list == nil {
   219  				list = &ast.ListLit{}
   220  				listMap[f.CUEName] = list
   221  				value = list
   222  			}
   223  
   224  			if len(x.Values) == 1 || f.ValueType == pbinternal.Message {
   225  				v := d.decodeValue(f, x)
   226  				if value == nil {
   227  					if cg := addComments(x.PreComments...); cg != nil {
   228  						cg.Doc = true
   229  						ast.AddComment(v, cg)
   230  					}
   231  				}
   232  				if cg := addComments(x.PostValuesComments...); cg != nil {
   233  					cg.Position = 4
   234  					ast.AddComment(v, cg)
   235  				}
   236  				list.Elts = append(list.Elts, v)
   237  				break
   238  			}
   239  
   240  			var last ast.Expr
   241  			// Handle [1, 2, 3]
   242  			for _, v := range x.Values {
   243  				if v.Value == "" {
   244  					if cg := addComments(v.PreComments...); cg != nil {
   245  						if last != nil {
   246  							cg.Position = 4
   247  							ast.AddComment(last, cg)
   248  						} else {
   249  							cg.Position = 1
   250  							ast.AddComment(list, cg)
   251  						}
   252  					}
   253  					continue
   254  				}
   255  				y := *x
   256  				y.Values = []*pbast.Value{v}
   257  				last = d.decodeValue(f, &y)
   258  				list.Elts = append(list.Elts, last)
   259  			}
   260  			if cg := addComments(x.PostValuesComments...); cg != nil {
   261  				if last != nil {
   262  					cg.Position = 4
   263  					ast.AddComment(last, cg)
   264  				} else {
   265  					cg.Position = 1
   266  					ast.AddComment(list, cg)
   267  				}
   268  			}
   269  			if cg := addComments(x.ClosingBraceComment); cg != nil {
   270  				cg.Position = 4
   271  				ast.AddComment(list, cg)
   272  			}
   273  
   274  		case pbinternal.Map:
   275  			// mapValue: {
   276  			//     key: 123
   277  			//     value: "string"
   278  			// }
   279  			if k := len(x.Values); k > 0 {
   280  				d.addErrf(x.Start, "values not allowed for Message type; found %d", k)
   281  			}
   282  
   283  			var (
   284  				key ast.Label
   285  				val ast.Expr
   286  			)
   287  
   288  			for _, c := range x.Children {
   289  				if len(c.Values) != 1 {
   290  					d.addErrf(x.Start, "expected 1 value, found %d", len(c.Values))
   291  					continue
   292  				}
   293  				s := c.Values[0].Value
   294  
   295  				switch c.Name {
   296  				case "key":
   297  					if strings.HasPrefix(s, `"`) {
   298  						key = &ast.BasicLit{Kind: token.STRING, Value: s}
   299  					} else {
   300  						key = ast.NewString(s)
   301  					}
   302  
   303  				case "value":
   304  					val = d.decodeValue(f, c)
   305  
   306  					if cg := addComments(x.ClosingBraceComment); cg != nil {
   307  						cg.Line = true
   308  						ast.AddComment(val, cg)
   309  					}
   310  
   311  				default:
   312  					d.addErrf(c.Start, "unsupported key name %q in map", c.Name)
   313  					continue
   314  				}
   315  			}
   316  
   317  			if key != nil && val != nil {
   318  				value = ast.NewStruct(key, val)
   319  			}
   320  		}
   321  
   322  		if value != nil {
   323  			var label ast.Label
   324  			if s := f.CUEName; ast.IsValidIdent(s) {
   325  				label = ast.NewIdent(s)
   326  			} else {
   327  				label = ast.NewString(s)
   328  
   329  			}
   330  			// TODO: convert line number information. However, position
   331  			// information in textpbfmt packages is too wonky to be useful
   332  			f := &ast.Field{
   333  				Label: label,
   334  				Value: value,
   335  				// Attrs: []*ast.Attribute{{Text: f.attr.}},
   336  			}
   337  			if cg := addComments(x.PreComments...); cg != nil {
   338  				cg.Doc = true
   339  				ast.AddComment(f, cg)
   340  			}
   341  			st.Elts = append(st.Elts, f)
   342  		}
   343  	}
   344  
   345  	return st
   346  }
   347  
   348  func addComments(lines ...string) (cg *ast.CommentGroup) {
   349  	var a []*ast.Comment
   350  	for _, c := range lines {
   351  		if !strings.HasPrefix(c, "#") {
   352  			continue
   353  		}
   354  		a = append(a, &ast.Comment{Text: "//" + c[1:]})
   355  	}
   356  	if a != nil {
   357  		cg = &ast.CommentGroup{List: a}
   358  	}
   359  	return cg
   360  }
   361  
   362  func (d *decoder) decodeValue(f *fieldInfo, n *pbast.Node) (x ast.Expr) {
   363  	if f.ValueType == pbinternal.Message {
   364  		if k := len(n.Values); k > 0 {
   365  			d.addErrf(n.Start, "values not allowed for Message type; found %d", k)
   366  		}
   367  		x = d.decodeMsg(f.msg, n.Children)
   368  		if cg := addComments(n.ClosingBraceComment); cg != nil {
   369  			cg.Line = true
   370  			cg.Position = 4
   371  			ast.AddComment(x, cg)
   372  		}
   373  		return x
   374  	}
   375  
   376  	if len(n.Values) != 1 {
   377  		d.addErrf(n.Start, "expected 1 value, found %d", len(n.Values))
   378  		return nil
   379  	}
   380  	v := n.Values[0]
   381  
   382  	defer func() {
   383  		if cg := addComments(v.PreComments...); cg != nil {
   384  			cg.Doc = true
   385  			ast.AddComment(x, cg)
   386  		}
   387  		if cg := addComments(v.InlineComment); cg != nil {
   388  			cg.Line = true
   389  			cg.Position = 2
   390  			ast.AddComment(x, cg)
   391  		}
   392  	}()
   393  
   394  	switch f.ValueType {
   395  	case pbinternal.String, pbinternal.Bytes:
   396  		s, err := unquote.Unquote(n)
   397  		if err != nil {
   398  			d.addErrf(n.Start, "invalid string or bytes: %v", err)
   399  		}
   400  		if f.ValueType == pbinternal.String {
   401  			s = literal.String.Quote(s)
   402  		} else {
   403  			s = literal.Bytes.Quote(s)
   404  		}
   405  		return &ast.BasicLit{Kind: token.STRING, Value: s}
   406  
   407  	case pbinternal.Bool:
   408  		switch v.Value {
   409  		case "true":
   410  			return ast.NewBool(true)
   411  
   412  		case "false":
   413  		default:
   414  			d.addErrf(n.Start, "invalid bool %s", v.Value)
   415  		}
   416  		return ast.NewBool(false)
   417  
   418  	case pbinternal.Int, pbinternal.Float:
   419  		s := v.Value
   420  		switch s {
   421  		case "inf", "nan":
   422  			// TODO: include message.
   423  			return &ast.BottomLit{}
   424  		}
   425  
   426  		var info literal.NumInfo
   427  		if err := literal.ParseNum(s, &info); err != nil {
   428  			var x ast.BasicLit
   429  			if pbinternal.MatchBySymbol(f.Value, s, &x) {
   430  				return &x
   431  			}
   432  			d.addErrf(n.Start, "invalid number %s", s)
   433  		}
   434  		if !info.IsInt() {
   435  			return &ast.BasicLit{Kind: token.FLOAT, Value: s}
   436  		}
   437  		return &ast.BasicLit{Kind: token.INT, Value: info.String()}
   438  
   439  	default:
   440  		panic(fmt.Sprintf("unexpected type %v", f.ValueType))
   441  	}
   442  }
   443  

View as plain text