cuelang.org/go@v0.13.0/internal/core/convert/go.go (about) 1 // Copyright 2019 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 convert allows converting to and from Go values and Types. 16 package convert 17 18 import ( 19 "encoding" 20 "encoding/json" 21 "fmt" 22 "math" 23 "math/big" 24 "reflect" 25 "slices" 26 "strconv" 27 "strings" 28 29 "github.com/cockroachdb/apd/v3" 30 "golang.org/x/text/encoding/unicode" 31 32 "cuelang.org/go/cue/ast" 33 "cuelang.org/go/cue/ast/astutil" 34 "cuelang.org/go/cue/errors" 35 "cuelang.org/go/cue/parser" 36 "cuelang.org/go/cue/token" 37 "cuelang.org/go/internal" 38 "cuelang.org/go/internal/core/adt" 39 "cuelang.org/go/internal/core/compile" 40 "cuelang.org/go/internal/types" 41 ) 42 43 // This file contains functionality for converting Go to CUE. 44 // 45 // The code in this file is a prototype implementation and is far from 46 // optimized. 47 48 func GoValueToValue(ctx *adt.OpContext, x interface{}, nilIsTop bool) adt.Value { 49 v := GoValueToExpr(ctx, nilIsTop, x) 50 // TODO: return Value 51 return toValue(v) 52 } 53 54 func GoTypeToExpr(ctx *adt.OpContext, x interface{}) (adt.Expr, errors.Error) { 55 v := newGoConverter(ctx).convertGoType(reflect.TypeOf(x)) 56 if err := ctx.Err(); err != nil { 57 return v, err.Err 58 } 59 return v, nil 60 } 61 62 type goConverter struct { 63 ctx *adt.OpContext 64 tfile *token.File 65 offset int 66 } 67 68 func newGoConverter(ctx *adt.OpContext) *goConverter { 69 return &goConverter{ 70 ctx: ctx, 71 // Code in *[token.File] uses size+1 in a few places. So do 72 // MaxInt-2 to be sure to avoid wrap-around issues. 73 tfile: token.NewFile(pkgID(), -1, math.MaxInt-2), 74 offset: 1, 75 } 76 } 77 78 func (c *goConverter) setNextPos(n ast.Node) ast.Node { 79 ast.SetPos(n, c.tfile.Pos(c.offset, 0)) 80 c.offset++ 81 return n 82 } 83 84 func toValue(e adt.Expr) adt.Value { 85 if v, ok := e.(adt.Value); ok { 86 return v 87 } 88 obj := &adt.Vertex{} 89 obj.AddConjunct(adt.MakeRootConjunct(nil, e)) 90 return obj 91 } 92 93 func compileExpr(ctx *adt.OpContext, expr ast.Expr) adt.Value { 94 c, err := compile.Expr(nil, ctx, pkgID(), expr) 95 if err != nil { 96 return &adt.Bottom{ 97 Err: errors.Promote(err, "compile")} 98 } 99 return adt.Resolve(ctx, c) 100 } 101 102 // parseTag parses a CUE expression from a cue tag. 103 func parseTag(ctx *adt.OpContext, field, tag string) ast.Expr { 104 tag, _ = splitTag(tag) 105 if tag == "" { 106 return topSentinel 107 } 108 expr, err := parser.ParseExpr("<field:>", tag) 109 if err != nil { 110 err := errors.Promote(err, "parser") 111 ctx.AddErr(errors.Wrapf(err, ctx.Pos(), 112 "invalid tag %q for field %q", tag, field)) 113 return &ast.BadExpr{} 114 } 115 return expr 116 } 117 118 // splitTag splits a cue tag into cue and options. 119 func splitTag(tag string) (cue string, options string) { 120 q := strings.LastIndexByte(tag, '"') 121 if c := strings.IndexByte(tag[q+1:], ','); c >= 0 { 122 return tag[:q+1+c], tag[q+1+c+1:] 123 } 124 return tag, "" 125 } 126 127 // TODO: should we allow mapping names in cue tags? This only seems like a good 128 // idea if we ever want to allow mapping CUE to a different name than JSON. 129 var tagsWithNames = []string{"json", "yaml", "protobuf"} 130 131 func getName(f *reflect.StructField) string { 132 name := f.Name 133 if f.Anonymous { 134 name = "" 135 } 136 for _, s := range tagsWithNames { 137 if tag, ok := f.Tag.Lookup(s); ok { 138 if p := strings.IndexByte(tag, ','); p >= 0 { 139 tag = tag[:p] 140 } 141 if tag != "" { 142 name = tag 143 break 144 } 145 } 146 } 147 return name 148 } 149 150 // isOptional indicates whether a field should be marked as optional. 151 func isOptional(f *reflect.StructField) bool { 152 isOptional := false 153 switch f.Type.Kind() { 154 case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Interface, reflect.Slice: 155 // Note: it may be confusing to distinguish between an empty slice and 156 // a nil slice. However, it is also surprising to not be able to specify 157 // a default value for a slice. So for now we will allow it. 158 isOptional = true 159 } 160 if tag, ok := f.Tag.Lookup("cue"); ok { 161 // TODO: only if first field is not empty. 162 _, opt := splitTag(tag) 163 isOptional = false 164 for _, f := range strings.Split(opt, ",") { 165 switch f { 166 case "opt": 167 isOptional = true 168 case "req": 169 return false 170 } 171 } 172 } else if tag, ok = f.Tag.Lookup("json"); ok { 173 isOptional = false 174 for _, f := range strings.Split(tag, ",")[1:] { 175 if f == "omitempty" { 176 return true 177 } 178 } 179 } 180 return isOptional 181 } 182 183 // isOmitEmpty means that the zero value is interpreted as undefined. 184 func isOmitEmpty(f *reflect.StructField) bool { 185 isOmitEmpty := false 186 switch f.Type.Kind() { 187 case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Interface, reflect.Slice: 188 // Note: it may be confusing to distinguish between an empty slice and 189 // a nil slice. However, it is also surprising to not be able to specify 190 // a default value for a slice. So for now we will allow it. 191 isOmitEmpty = true 192 193 default: 194 // TODO: we can also infer omit empty if a type cannot be nil if there 195 // is a constraint that unconditionally disallows the zero value. 196 } 197 tag, ok := f.Tag.Lookup("json") 198 if ok { 199 isOmitEmpty = false 200 for _, f := range strings.Split(tag, ",")[1:] { 201 if f == "omitempty" { 202 return true 203 } 204 } 205 } 206 return isOmitEmpty 207 } 208 209 func GoValueToExpr(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Expr { 210 e := newGoConverter(ctx).convertRec(nilIsTop, x) 211 if e == nil { 212 return ctx.AddErrf("unsupported Go type (%T)", x) 213 } 214 return e 215 } 216 217 func isNil(x reflect.Value) bool { 218 switch x.Kind() { 219 // Only check for supported types; ignore func and chan. 220 case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface: 221 return x.IsNil() 222 } 223 return false 224 } 225 226 func (c *goConverter) convertRec(nilIsTop bool, x interface{}) (result adt.Value) { 227 if t := (&types.Value{}); types.CastValue(t, x) { 228 // TODO: panic if not the same runtime. 229 return t.V 230 } 231 src := c.ctx.Source() 232 233 switch v := x.(type) { 234 case nil: 235 if nilIsTop { 236 ident, _ := src.(*ast.Ident) 237 return &adt.Top{Src: ident} 238 } 239 return &adt.Null{Src: src} 240 241 case *ast.File: 242 x, err := compile.Files(nil, c.ctx, pkgID(), v) 243 if err != nil { 244 return &adt.Bottom{Err: errors.Promote(err, "compile")} 245 } 246 if _, n := x.SingleConjunct(); n != 1 { 247 panic("unexpected length") 248 } 249 return x 250 251 case ast.Expr: 252 return compileExpr(c.ctx, v) 253 254 case *big.Int: 255 v2 := new(apd.BigInt).SetMathBigInt(v) 256 return &adt.Num{ 257 Src: src, 258 K: adt.IntKind, 259 X: *apd.NewWithBigInt(v2, 0), 260 } 261 262 case *big.Rat: 263 // should we represent this as a binary operation? 264 n := &adt.Num{Src: src, K: adt.IntKind} 265 _, err := internal.BaseContext.Quo(&n.X, 266 apd.NewWithBigInt(new(apd.BigInt).SetMathBigInt(v.Num()), 0), 267 apd.NewWithBigInt(new(apd.BigInt).SetMathBigInt(v.Denom()), 0), 268 ) 269 if err != nil { 270 return c.ctx.AddErrf("could not convert *big.Rat: %v", err) 271 } 272 if !v.IsInt() { 273 n.K = adt.FloatKind 274 } 275 return n 276 277 case *big.Float: 278 n := &adt.Num{Src: src, K: adt.FloatKind} 279 _, _, err := n.X.SetString(v.String()) 280 if err != nil { 281 return c.ctx.AddErr(errors.Promote(err, "invalid float")) 282 } 283 return n 284 285 case *apd.Decimal: 286 // TODO: should we allow an "int" bit to be set here? It is a bit 287 // tricky, as we would also need to pass down the result of rounding. 288 // So more likely an API must return explicitly whether a value is 289 // a float or an int after all. 290 // The code to autodetect whether something is an integer can be done 291 // with this: 292 kind := adt.FloatKind 293 var d apd.Decimal 294 res, _ := internal.BaseContext.RoundToIntegralExact(&d, v) 295 if !res.Inexact() { 296 kind = adt.IntKind 297 v = &d 298 } 299 n := &adt.Num{Src: src, K: kind} 300 n.X = *v 301 return n 302 303 case json.Marshaler: 304 b, err := v.MarshalJSON() 305 if err != nil { 306 return c.ctx.AddErr(errors.Promote(err, "json.Marshaler")) 307 } 308 expr, err := parser.ParseExpr("json", b) 309 if err != nil { 310 panic(err) // cannot happen 311 } 312 return compileExpr(c.ctx, expr) 313 314 case encoding.TextMarshaler: 315 b, err := v.MarshalText() 316 if err != nil { 317 return c.ctx.AddErr(errors.Promote(err, "encoding.TextMarshaler")) 318 } 319 s, _ := unicode.UTF8.NewEncoder().String(string(b)) 320 return &adt.String{Src: src, Str: s} 321 322 case error: 323 var errs errors.Error 324 switch x := v.(type) { 325 case errors.Error: 326 errs = x 327 default: 328 errs = c.ctx.Newf("%s", x.Error()) 329 } 330 return &adt.Bottom{Err: errs} 331 case bool: 332 return &adt.Bool{Src: src, B: v} 333 case string: 334 s, _ := unicode.UTF8.NewEncoder().String(v) 335 return &adt.String{Src: src, Str: s} 336 case []byte: 337 return &adt.Bytes{Src: src, B: v} 338 case int: 339 return c.toInt(int64(v)) 340 case int8: 341 return c.toInt(int64(v)) 342 case int16: 343 return c.toInt(int64(v)) 344 case int32: 345 return c.toInt(int64(v)) 346 case int64: 347 return c.toInt(v) 348 case uint: 349 return c.toUint(uint64(v)) 350 case uint8: 351 return c.toUint(uint64(v)) 352 case uint16: 353 return c.toUint(uint64(v)) 354 case uint32: 355 return c.toUint(uint64(v)) 356 case uint64: 357 return c.toUint(v) 358 case uintptr: 359 return c.toUint(uint64(v)) 360 case float64: 361 n := &adt.Num{Src: src, K: adt.FloatKind} 362 _, err := n.X.SetFloat64(v) 363 if err != nil { 364 return c.ctx.AddErr(errors.Promote(err, "invalid float")) 365 } 366 return n 367 case float32: 368 n := &adt.Num{Src: src, K: adt.FloatKind} 369 // apd.Decimal has a SetFloat64 method, but no SetFloat32. 370 _, _, err := n.X.SetString(strconv.FormatFloat(float64(v), 'E', -1, 32)) 371 if err != nil { 372 return c.ctx.AddErr(errors.Promote(err, "invalid float")) 373 } 374 return n 375 376 case reflect.Value: 377 if v.CanInterface() { 378 return c.convertRec(nilIsTop, v.Interface()) 379 } 380 381 default: 382 value := reflect.ValueOf(v) 383 switch value.Kind() { 384 case reflect.Bool: 385 return &adt.Bool{Src: src, B: value.Bool()} 386 387 case reflect.String: 388 str := value.String() 389 str, _ = unicode.UTF8.NewEncoder().String(str) 390 // TODO: here and above: allow to fail on invalid strings. 391 // if !utf8.ValidString(str) { 392 // return ctx.AddErrf("cannot convert result to string: invalid UTF-8") 393 // } 394 return &adt.String{Src: src, Str: str} 395 396 case reflect.Int, reflect.Int8, reflect.Int16, 397 reflect.Int32, reflect.Int64: 398 return c.toInt(value.Int()) 399 400 case reflect.Uint, reflect.Uint8, reflect.Uint16, 401 reflect.Uint32, reflect.Uint64, reflect.Uintptr: 402 return c.toUint(value.Uint()) 403 404 case reflect.Float32, reflect.Float64: 405 return c.convertRec(nilIsTop, value.Float()) 406 407 case reflect.Ptr: 408 if value.IsNil() { 409 if nilIsTop { 410 ident, _ := src.(*ast.Ident) 411 return &adt.Top{Src: ident} 412 } 413 return &adt.Null{Src: src} 414 } 415 return c.convertRec(nilIsTop, value.Elem().Interface()) 416 417 case reflect.Struct: 418 sl := &adt.StructLit{Src: c.setNextPos(ast.NewStruct())} 419 sl.Init(c.ctx) 420 v := &adt.Vertex{} 421 422 t := value.Type() 423 for i := 0; i < value.NumField(); i++ { 424 sf := t.Field(i) 425 if sf.PkgPath != "" { 426 continue 427 } 428 val := value.Field(i) 429 if !nilIsTop && isNil(val) { 430 continue 431 } 432 if tag, _ := sf.Tag.Lookup("json"); tag == "-" { 433 continue 434 } 435 if isOmitEmpty(&sf) && val.IsZero() { 436 continue 437 } 438 sub := c.convertRec(nilIsTop, val.Interface()) 439 if sub == nil { 440 // mimic behavior of encoding/json: skip fields of unsupported types 441 continue 442 } 443 if _, ok := sub.(*adt.Bottom); ok { 444 return sub 445 } 446 447 // leave errors like we do during normal evaluation or do we 448 // want to return the error? 449 name := getName(&sf) 450 if name == "-" { 451 continue 452 } 453 if sf.Anonymous && name == "" { 454 arc, ok := sub.(*adt.Vertex) 455 if ok { 456 v.Arcs = append(v.Arcs, arc.Arcs...) 457 } 458 continue 459 } 460 461 f := c.ctx.StringLabel(name) 462 c.createField(f, sub, sl) 463 v.Arcs = append(v.Arcs, c.ensureArcVertex(sub, f)) 464 } 465 466 env := c.ctx.Env(0) 467 if env == nil { 468 env = &adt.Environment{} 469 } 470 // There is no closedness or cycle info for Go structs, so we 471 // pass an empty CloseInfo. 472 v.AddStruct(sl, env, adt.CloseInfo{}) 473 v.SetValue(c.ctx, &adt.StructMarker{}) 474 v.ForceDone() 475 476 return v 477 478 case reflect.Map: 479 obj := &adt.StructLit{Src: c.setNextPos(ast.NewStruct())} 480 obj.Init(c.ctx) 481 v := &adt.Vertex{} 482 483 t := value.Type() 484 switch key := t.Key(); key.Kind() { 485 default: 486 if !key.Implements(textMarshaler) { 487 return c.ctx.AddErrf("unsupported Go type for map key (%v)", key) 488 } 489 fallthrough 490 case reflect.String, 491 reflect.Int, reflect.Int8, reflect.Int16, 492 reflect.Int32, reflect.Int64, 493 reflect.Uint, reflect.Uint8, reflect.Uint16, 494 reflect.Uint32, reflect.Uint64, reflect.Uintptr: 495 496 iter := value.MapRange() 497 for iter.Next() { 498 k := iter.Key() 499 val := iter.Value() 500 // if isNil(val) { 501 // continue 502 // } 503 504 sub := c.convertRec(nilIsTop, val.Interface()) 505 // mimic behavior of encoding/json: report error of 506 // unsupported type. 507 if sub == nil { 508 return c.ctx.AddErrf("unsupported Go type (%T)", val.Interface()) 509 } 510 if isBottom(sub) { 511 return sub 512 } 513 514 s := fmt.Sprint(k) 515 f := c.ctx.StringLabel(s) 516 v.Arcs = append(v.Arcs, c.ensureArcVertex(sub, f)) 517 } 518 slices.SortFunc(v.Arcs, func(a, b *adt.Vertex) int { 519 return strings.Compare(a.Label.IdentString(c.ctx), b.Label.IdentString(c.ctx)) 520 }) 521 // Create all the adt/ast fields after sorting the arcs 522 for _, arc := range v.Arcs { 523 c.createField(arc.Label, arc, obj) 524 } 525 } 526 527 env := c.ctx.Env(0) 528 if env == nil { 529 env = &adt.Environment{} 530 } 531 v.AddStruct(obj, env, adt.CloseInfo{}) 532 v.SetValue(c.ctx, &adt.StructMarker{}) 533 v.ForceDone() 534 535 return v 536 537 case reflect.Slice, reflect.Array: 538 list := &adt.ListLit{Src: ast.NewList()} 539 c.setNextPos(list.Src) 540 541 v := &adt.Vertex{} 542 543 for i := 0; i < value.Len(); i++ { 544 val := value.Index(i) 545 x := c.convertRec(nilIsTop, val.Interface()) 546 if x == nil { 547 return c.ctx.AddErrf("unsupported Go type (%T)", 548 val.Interface()) 549 } 550 if isBottom(x) { 551 return x 552 } 553 list.Elems = append(list.Elems, x) 554 f := adt.MakeIntLabel(adt.IntLabel, int64(i)) 555 v.Arcs = append(v.Arcs, c.ensureArcVertex(x, f)) 556 } 557 558 env := c.ctx.Env(0) 559 if env == nil { 560 env = &adt.Environment{} 561 } 562 v.AddConjunct(adt.MakeRootConjunct(env, list)) 563 v.SetValue(c.ctx, &adt.ListMarker{}) 564 v.ForceDone() 565 566 return v 567 } 568 } 569 return nil 570 } 571 572 func (c *goConverter) ensureArcVertex(x adt.Value, l adt.Feature) *adt.Vertex { 573 env := c.ctx.Env(0) 574 if env == nil { 575 env = &adt.Environment{} 576 } 577 arc, ok := x.(*adt.Vertex) 578 if ok { 579 a := *arc 580 arc = &a 581 arc.Label = l 582 } else { 583 arc = &adt.Vertex{Label: l} 584 arc.AddConjunct(adt.MakeRootConjunct(env, x)) 585 arc.SetValue(c.ctx, x) 586 arc.ForceDone() 587 } 588 return arc 589 } 590 591 func (c *goConverter) createField(l adt.Feature, sub adt.Value, sl *adt.StructLit) { 592 src := sl.Src.(*ast.StructLit) 593 astField := &ast.Field{ 594 Label: ast.NewIdent(l.IdentString(c.ctx)), 595 Constraint: token.ILLEGAL, 596 } 597 if expr, ok := sub.Source().(ast.Expr); ok { 598 astField.Value = expr 599 } 600 c.setNextPos(astField.Label) 601 src.Elts = append(src.Elts, astField) 602 field := &adt.Field{Label: l, Value: sub, Src: astField} 603 sl.Decls = append(sl.Decls, field) 604 } 605 606 func (c *goConverter) toInt(x int64) adt.Value { 607 n := &adt.Num{Src: c.ctx.Source(), K: adt.IntKind} 608 n.X = *apd.New(x, 0) 609 return n 610 } 611 612 func (c *goConverter) toUint(x uint64) adt.Value { 613 n := &adt.Num{Src: c.ctx.Source(), K: adt.IntKind} 614 n.X.Coeff.SetUint64(x) 615 return n 616 } 617 618 func (c *goConverter) convertGoType(t reflect.Type) adt.Expr { 619 // TODO: this can be much more efficient. 620 // TODO: synchronize 621 return c.goTypeToValue(true, t) 622 } 623 624 var ( 625 jsonMarshaler = reflect.TypeFor[json.Marshaler]() 626 textMarshaler = reflect.TypeFor[encoding.TextMarshaler]() 627 topSentinel = ast.NewIdent("_") 628 ) 629 630 // goTypeToValue converts a Go Type to a value. 631 // 632 // TODO: if this value will always be unified with a concrete type in Go, then 633 // many of the fields may be omitted. 634 func (c *goConverter) goTypeToValue(allowNullDefault bool, t reflect.Type) adt.Expr { 635 if _, t, ok := c.ctx.LoadType(t); ok { 636 return t 637 } 638 639 _, v := c.goTypeToValueRec(allowNullDefault, t) 640 if v == nil { 641 return c.ctx.AddErrf("unsupported Go type (%v)", t) 642 } 643 return v 644 } 645 646 func (c *goConverter) goTypeToValueRec(allowNullDefault bool, t reflect.Type) (e ast.Expr, expr adt.Expr) { 647 if src, t, ok := c.ctx.LoadType(t); ok { 648 return src, t 649 } 650 651 switch reflect.Zero(t).Interface().(type) { 652 case *big.Int, big.Int: 653 e = ast.NewIdent("int") 654 goto store 655 656 case *big.Float, big.Float, *big.Rat, big.Rat: 657 e = ast.NewIdent("number") 658 goto store 659 660 case *apd.Decimal, apd.Decimal: 661 e = ast.NewIdent("number") 662 goto store 663 } 664 665 // Even if this is for types that we know cast to a certain type, it can't 666 // hurt to return top, as in these cases the concrete values will be 667 // strict instances and there cannot be any tags that further constrain 668 // the values. 669 if t.Implements(jsonMarshaler) || t.Implements(textMarshaler) { 670 e = topSentinel 671 goto store 672 } 673 674 switch k := t.Kind(); k { 675 case reflect.Ptr: 676 elem := t.Elem() 677 for elem.Kind() == reflect.Ptr { 678 elem = elem.Elem() 679 } 680 e, _ = c.goTypeToValueRec(false, elem) 681 if allowNullDefault { 682 e = wrapOrNull(e) 683 } 684 685 case reflect.Interface: 686 switch t.Name() { 687 case "error": 688 // This is really null | _|_. There is no error if the error is null. 689 e = ast.NewNull() 690 default: 691 e = topSentinel // `_` 692 } 693 694 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 695 reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 696 e = compile.LookupRange(t.Kind().String()).Source().(ast.Expr) 697 698 case reflect.Uint, reflect.Uintptr: 699 e = compile.LookupRange("uint64").Source().(ast.Expr) 700 701 case reflect.Int: 702 e = compile.LookupRange("int64").Source().(ast.Expr) 703 704 case reflect.String: 705 e = ast.NewIdent("__string") 706 707 case reflect.Bool: 708 e = ast.NewIdent("__bool") 709 710 case reflect.Float32, reflect.Float64: 711 e = ast.NewIdent("__number") 712 713 case reflect.Struct: 714 obj := &ast.StructLit{} 715 716 // TODO: dirty trick: set this to a temporary Vertex and then update the 717 // arcs and conjuncts of this vertex below. This will allow circular 718 // references. Maybe have a special kind of "hardlink" reference. 719 c.ctx.StoreType(t, obj, nil) 720 721 for i := 0; i < t.NumField(); i++ { 722 f := t.Field(i) 723 if f.PkgPath != "" { 724 continue 725 } 726 _, ok := f.Tag.Lookup("cue") 727 elem, _ := c.goTypeToValueRec(!ok, f.Type) 728 if isBad(elem) { 729 continue // Ignore fields for unsupported types 730 } 731 732 // leave errors like we do during normal evaluation or do we 733 // want to return the error? 734 name := getName(&f) 735 if name == "-" { 736 continue 737 } 738 739 if tag, ok := f.Tag.Lookup("cue"); ok { 740 v := parseTag(c.ctx, name, tag) 741 if isBad(v) { 742 return v, nil 743 } 744 elem = ast.NewBinExpr(token.AND, elem, v) 745 } 746 // TODO: if an identifier starts with __ (or otherwise is not a 747 // valid CUE name), make it a string and create a map to a new 748 // name for references. 749 750 // The GO JSON decoder always allows a value to be undefined. 751 d := &ast.Field{Label: ast.NewIdent(name), Value: elem} 752 c.setNextPos(d) 753 if isOptional(&f) { 754 internal.SetConstraint(d, token.OPTION) 755 } 756 obj.Elts = append(obj.Elts, d) 757 } 758 759 // TODO: should we validate references here? Can be done using 760 // astutil.ToFile and astutil.Resolve. 761 762 e = obj 763 764 case reflect.Array, reflect.Slice: 765 if t.Elem().Kind() == reflect.Uint8 { 766 e = ast.NewIdent("__bytes") 767 } else { 768 elem, _ := c.goTypeToValueRec(allowNullDefault, t.Elem()) 769 if elem == nil { 770 b := c.ctx.AddErrf("unsupported Go type (%v)", t.Elem()) 771 return &ast.BadExpr{}, b 772 } 773 774 if t.Kind() == reflect.Array { 775 e = ast.NewCall( 776 ast.NewSel(&ast.Ident{ 777 Name: "list", 778 Node: ast.NewImport(nil, "list")}, 779 "Repeat"), 780 ast.NewList(elem), 781 ast.NewLit(token.INT, strconv.Itoa(t.Len()))) 782 } else { 783 e = ast.NewList(&ast.Ellipsis{Type: elem}) 784 } 785 } 786 if k == reflect.Slice { 787 e = wrapOrNull(e) 788 } 789 790 case reflect.Map: 791 switch key := t.Key(); key.Kind() { 792 case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, 793 reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, 794 reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 795 default: 796 b := c.ctx.AddErrf("unsupported Go type for map key (%v)", key) 797 return &ast.BadExpr{}, b 798 } 799 800 v, x := c.goTypeToValueRec(allowNullDefault, t.Elem()) 801 if v == nil { 802 b := c.ctx.AddErrf("unsupported Go type (%v)", t.Elem()) 803 return &ast.BadExpr{}, b 804 } 805 if isBad(v) { 806 return v, x 807 } 808 809 e = ast.NewStruct(&ast.Field{ 810 Label: ast.NewList(ast.NewIdent("__string")), 811 Value: v, 812 }) 813 814 e = wrapOrNull(e) 815 } 816 817 store: 818 // TODO: store error if not nil? 819 if e != nil { 820 c.setNextPos(e) 821 f := &ast.File{Decls: []ast.Decl{&ast.EmbedDecl{Expr: e}}} 822 astutil.Resolve(f, func(_ token.Pos, msg string, args ...interface{}) { 823 c.ctx.AddErrf(msg, args...) 824 }) 825 var x adt.Expr 826 x2, err := compile.Expr(nil, c.ctx, pkgID(), e) 827 if err != nil { 828 b := &adt.Bottom{Err: err} 829 c.ctx.AddBottom(b) 830 x = b 831 } else { 832 x = x2.Expr() 833 } 834 c.ctx.StoreType(t, e, x) 835 return e, x 836 } 837 return e, nil 838 } 839 840 func isBottom(x adt.Node) bool { 841 if x == nil { 842 return true 843 } 844 b, _ := x.(*adt.Bottom) 845 return b != nil 846 } 847 848 func isBad(x ast.Expr) bool { 849 if x == nil { 850 return true 851 } 852 if bad, _ := x.(*ast.BadExpr); bad != nil { 853 return true 854 } 855 return false 856 } 857 858 func wrapOrNull(e ast.Expr) ast.Expr { 859 switch x := e.(type) { 860 case *ast.BasicLit: 861 if x.Kind == token.NULL { 862 return x 863 } 864 case *ast.BadExpr: 865 return e 866 } 867 return makeNullable(e, true) 868 } 869 870 func makeNullable(e ast.Expr, nullIsDefault bool) ast.Expr { 871 var null ast.Expr = ast.NewNull() 872 if nullIsDefault { 873 null = &ast.UnaryExpr{Op: token.MUL, X: null} 874 } 875 return ast.NewBinExpr(token.OR, null, e) 876 } 877 878 // pkgID returns a package path that can never resolve to an existing package. 879 func pkgID() string { 880 return "_" 881 }