1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package jsonpb
16
17 import (
18 "encoding/base64"
19 "strings"
20
21 "cuelang.org/go/cue"
22 "cuelang.org/go/cue/ast"
23 "cuelang.org/go/cue/ast/astutil"
24 "cuelang.org/go/cue/errors"
25 "cuelang.org/go/cue/literal"
26 "cuelang.org/go/cue/token"
27 "cuelang.org/go/encoding/protobuf/pbinternal"
28 "github.com/cockroachdb/apd/v3"
29 )
30
31
32
33
34 type Option func()
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 type Decoder struct {
68 schema cue.Value
69 }
70
71
72 func NewDecoder(schema cue.Value, options ...Option) *Decoder {
73 return &Decoder{schema: schema}
74 }
75
76
77
78
79
80
81
82 func (d *Decoder) RewriteFile(file *ast.File) error {
83 var r rewriter
84 r.rewriteDecls(d.schema, file.Decls)
85 return r.errs
86 }
87
88
89
90
91
92
93
94 func (d *Decoder) RewriteExpr(expr ast.Expr) (ast.Expr, error) {
95 var r rewriter
96 x := r.rewrite(d.schema, expr)
97 return x, r.errs
98 }
99
100 type rewriter struct {
101 errs errors.Error
102 }
103
104 func (r *rewriter) addErr(err errors.Error) {
105 r.errs = errors.Append(r.errs, err)
106 }
107
108 func (r *rewriter) addErrf(p token.Pos, schema cue.Value, format string, args ...interface{}) {
109 format = "%s: " + format
110 args = append([]interface{}{schema.Path()}, args...)
111 r.addErr(errors.Newf(p, format, args...))
112 }
113
114 func (r *rewriter) rewriteDecls(schema cue.Value, decls []ast.Decl) {
115 for _, f := range decls {
116 field, ok := f.(*ast.Field)
117 if !ok {
118 continue
119 }
120 sel := cue.Label(field.Label)
121 if !sel.IsString() {
122 continue
123 }
124
125 v := schema.LookupPath(cue.MakePath(sel))
126 if !v.Exists() {
127 f := schema.Template()
128 if f == nil {
129 continue
130 }
131 v = f(sel.String())
132 }
133 if !v.Exists() {
134 continue
135 }
136
137 field.Value = r.rewrite(v, field.Value)
138 }
139 }
140
141 var enumValuePath = cue.ParsePath("#enumValue").Optional()
142
143 func (r *rewriter) rewrite(schema cue.Value, expr ast.Expr) (x ast.Expr) {
144 defer func() {
145 if expr != x && x != nil {
146 astutil.CopyMeta(x, expr)
147 }
148 }()
149
150 switch x := expr.(type) {
151 case *ast.BasicLit:
152 if x.Kind != token.NULL {
153 break
154 }
155 if schema.IncompleteKind()&cue.NullKind != 0 {
156 break
157 }
158 switch v, _ := schema.Default(); {
159 case v.IsConcrete():
160 if x, _ := v.Syntax(cue.Final()).(ast.Expr); x != nil {
161 return x
162 }
163 default:
164 if x := zeroValue(schema, x); x != nil {
165 return x
166 }
167 }
168
169 case *ast.StructLit:
170 r.rewriteDecls(schema, x.Elts)
171 return x
172
173 case *ast.ListLit:
174 elem, _ := schema.Elem()
175 iter, _ := schema.List()
176 for i, e := range x.Elts {
177 v := elem
178 if iter.Next() {
179 v = iter.Value()
180 }
181 if !v.Exists() {
182 break
183 }
184 x.Elts[i] = r.rewrite(v, e)
185 }
186
187 return x
188 }
189
190 switch schema.IncompleteKind() {
191 case cue.IntKind, cue.FloatKind, cue.NumberKind:
192 x, q, str := stringValue(expr)
193 if x == nil || !q.IsDouble() {
194 break
195 }
196
197 var info literal.NumInfo
198 if err := literal.ParseNum(str, &info); err == nil {
199 x.Value = str
200 x.Kind = token.FLOAT
201 if info.IsInt() {
202 x.Kind = token.INT
203 }
204 break
205 }
206
207 pbinternal.MatchBySymbol(schema, str, x)
208
209 case cue.BytesKind:
210 x, q, str := stringValue(expr)
211 if x == nil && q.IsDouble() {
212 break
213 }
214
215 var b []byte
216 var err error
217 for _, enc := range base64Encodings {
218 if b, err = enc.DecodeString(str); err == nil {
219 break
220 }
221 }
222 if err != nil {
223 r.addErrf(expr.Pos(), schema, "failed to decode base64: %v", err)
224 return expr
225 }
226
227 quoter := literal.Bytes
228 if q.IsMulti() {
229 ws := q.Whitespace()
230 tabs := (strings.Count(ws, " ")+3)/4 + strings.Count(ws, "\t")
231 quoter = quoter.WithTabIndent(tabs)
232 }
233 x.Value = quoter.Quote(string(b))
234 return x
235
236 case cue.StringKind:
237 if s, ok := expr.(*ast.BasicLit); ok && s.Kind == token.INT {
238 var info literal.NumInfo
239 if err := literal.ParseNum(s.Value, &info); err != nil || !info.IsInt() {
240 break
241 }
242 var d apd.Decimal
243 if err := info.Decimal(&d); err != nil {
244 break
245 }
246 enum, err := d.Int64()
247 if err != nil {
248 r.addErrf(expr.Pos(), schema, "invalid enum index: %v", err)
249 return expr
250 }
251 op, values := schema.Expr()
252 if op != cue.OrOp {
253 values = []cue.Value{schema}
254 }
255 for _, v := range values {
256 i, err := v.LookupPath(enumValuePath).Int64()
257 if err == nil && i == enum {
258 str, err := v.String()
259 if err != nil {
260 r.addErr(errors.Wrapf(err, v.Pos(), "invalid string enum"))
261 return expr
262 }
263 s.Kind = token.STRING
264 s.Value = literal.String.Quote(str)
265
266 return s
267 }
268 }
269 r.addErrf(expr.Pos(), schema,
270 "could not locate integer enum value %d", enum)
271 }
272
273 case cue.StructKind, cue.TopKind:
274
275 }
276 return expr
277 }
278
279 func zeroValue(v cue.Value, x *ast.BasicLit) ast.Expr {
280 switch v.IncompleteKind() {
281 case cue.StringKind:
282 x.Kind = token.STRING
283 x.Value = `""`
284
285 case cue.BytesKind:
286 x.Kind = token.STRING
287 x.Value = `''`
288
289 case cue.BoolKind:
290 x.Kind = token.FALSE
291 x.Value = "false"
292
293 case cue.NumberKind, cue.IntKind, cue.FloatKind:
294 x.Kind = token.INT
295 x.Value = "0"
296
297 case cue.StructKind:
298 return ast.NewStruct()
299
300 case cue.ListKind:
301 return &ast.ListLit{}
302
303 default:
304 return nil
305 }
306 return x
307 }
308
309 func stringValue(x ast.Expr) (b *ast.BasicLit, q literal.QuoteInfo, str string) {
310 b, ok := x.(*ast.BasicLit)
311 if !ok || b.Kind != token.STRING {
312 return nil, q, ""
313 }
314 q, p, _, err := literal.ParseQuotes(b.Value, b.Value)
315 if err != nil {
316 return nil, q, ""
317 }
318
319 str, err = q.Unquote(b.Value[p:])
320 if err != nil {
321 return nil, q, ""
322 }
323
324 return b, q, str
325 }
326
327
328 var base64Encodings = []base64.Encoding{
329 *base64.StdEncoding,
330 *base64.URLEncoding,
331 *base64.RawStdEncoding,
332 *base64.RawURLEncoding,
333 }
334
View as plain text