1
2
3
4
5
6
7
8
9
10
11
12
13
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
36
37 type Option func(*options)
38
39 type options struct {
40 }
41
42
43 func NewDecoder(option ...Option) *Decoder {
44 d := &Decoder{}
45 _ = d.m
46 return d
47 }
48
49
50 type Decoder struct {
51 m map[*adt.Vertex]*mapping
52 }
53
54 type decoder struct {
55 *Decoder
56
57
58 errs errors.Error
59 file *token.File
60 }
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 func (d *Decoder) Parse(schema cue.Value, filename string, b []byte) (ast.Expr, error) {
76 dec := decoder{Decoder: d}
77
78
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
104
105
106
107 type mapping struct {
108 children map[string]*fieldInfo
109 }
110
111 type fieldInfo struct {
112 pbinternal.Info
113 msg *mapping
114
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
131
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
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
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
276
277
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
331
332 f := &ast.Field{
333 Label: label,
334 Value: value,
335
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
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