github.com/kubevela/workflow@v0.6.0/pkg/cue/model/value/value.go (about) 1 /* 2 Copyright 2022 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package value 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "sort" 23 "strconv" 24 "strings" 25 26 "cuelang.org/go/cue" 27 "cuelang.org/go/cue/ast" 28 "cuelang.org/go/cue/build" 29 "cuelang.org/go/cue/cuecontext" 30 "cuelang.org/go/cue/format" 31 "cuelang.org/go/cue/literal" 32 "cuelang.org/go/cue/parser" 33 "github.com/cue-exp/kubevelafix" 34 "github.com/pkg/errors" 35 36 "github.com/kubevela/workflow/pkg/cue/model/sets" 37 "github.com/kubevela/workflow/pkg/cue/packages" 38 "github.com/kubevela/workflow/pkg/stdlib" 39 ) 40 41 // DefaultPackageHeader describes the default package header for CUE files. 42 const DefaultPackageHeader = "package main\n" 43 44 // Value is an object with cue.context and vendors 45 type Value struct { 46 v cue.Value 47 r *cue.Context 48 field string 49 addImports func(instance *build.Instance) error 50 } 51 52 // String return value's cue format string 53 func (val *Value) String(opts ...func(node ast.Node) ast.Node) (string, error) { 54 opts = append(opts, sets.OptBytesToString) 55 return sets.ToString(val.v, opts...) 56 } 57 58 // Error return value's error information. 59 func (val *Value) Error() error { 60 v := val.CueValue() 61 if !v.Exists() { 62 return errors.New("empty value") 63 } 64 if err := val.v.Err(); err != nil { 65 return err 66 } 67 var gerr error 68 v.Walk(func(value cue.Value) bool { 69 if err := value.Eval().Err(); err != nil { 70 gerr = err 71 return false 72 } 73 return true 74 }, nil) 75 return gerr 76 } 77 78 // UnmarshalTo unmarshal value into golang object 79 func (val *Value) UnmarshalTo(x interface{}) error { 80 data, err := val.v.MarshalJSON() 81 if err != nil { 82 return err 83 } 84 return json.Unmarshal(data, x) 85 } 86 87 // SubstituteInStruct substitute expr in struct lit value 88 // nolint:staticcheck 89 func (val *Value) SubstituteInStruct(expr ast.Expr, key string) error { 90 node := val.CueValue().Syntax(cue.ResolveReferences(true)) 91 x, ok := node.(*ast.StructLit) 92 if !ok { 93 return errors.New("value is not a struct lit") 94 } 95 for i := range x.Elts { 96 if field, ok := x.Elts[i].(*ast.Field); ok { 97 if strings.Trim(sets.LabelStr(field.Label), `"`) == strings.Trim(key, `"`) { 98 x.Elts[i].(*ast.Field).Value = expr 99 b, err := format.Node(node) 100 if err != nil { 101 return err 102 } 103 val.v = val.r.CompileBytes(b) 104 return nil 105 } 106 } 107 } 108 return errors.New("key not found in struct") 109 } 110 111 // FieldName return value's field name 112 func (val *Value) FieldName() string { 113 return val.field 114 } 115 116 // NewValue new a value 117 func NewValue(s string, pd *packages.PackageDiscover, tagTempl string, opts ...func(*ast.File) error) (*Value, error) { 118 builder := &build.Instance{} 119 120 file, err := parser.ParseFile("-", s, parser.ParseComments) 121 if err != nil { 122 return nil, err 123 } 124 file = kubevelafix.Fix(file).(*ast.File) 125 for _, opt := range opts { 126 if err := opt(file); err != nil { 127 return nil, err 128 } 129 } 130 if err := builder.AddSyntax(file); err != nil { 131 return nil, err 132 } 133 return newValue(builder, pd, tagTempl) 134 } 135 136 // NewValueWithInstance new value with instance 137 func NewValueWithInstance(instance *build.Instance, pd *packages.PackageDiscover, tagTempl string) (*Value, error) { 138 return newValue(instance, pd, tagTempl) 139 } 140 141 func newValue(builder *build.Instance, pd *packages.PackageDiscover, tagTempl string) (*Value, error) { 142 addImports := func(inst *build.Instance) error { 143 if pd != nil { 144 pd.ImportBuiltinPackagesFor(inst) 145 } 146 if err := stdlib.AddImportsFor(inst, tagTempl); err != nil { 147 return err 148 } 149 return nil 150 } 151 152 if err := addImports(builder); err != nil { 153 return nil, err 154 } 155 156 r := cuecontext.New() 157 inst := r.BuildInstance(builder) 158 val := new(Value) 159 val.r = r 160 val.v = inst 161 val.addImports = addImports 162 // do not check val.Err() error here, because the value may be filled later 163 return val, nil 164 } 165 166 // AddFile add file to the instance 167 func AddFile(bi *build.Instance, filename string, src interface{}) error { 168 if filename == "" { 169 filename = "-" 170 } 171 file, err := parser.ParseFile(filename, src, parser.ParseComments) 172 file = kubevelafix.Fix(file).(*ast.File) 173 if err != nil { 174 return err 175 } 176 if err := bi.AddSyntax(file); err != nil { 177 return err 178 } 179 return nil 180 } 181 182 // TagFieldOrder add step tag. 183 func TagFieldOrder(root *ast.File) error { 184 i := 0 185 vs := &visitor{ 186 r: map[string]struct{}{}, 187 } 188 for _, decl := range root.Decls { 189 vs.addAttrForExpr(decl, &i) 190 } 191 return nil 192 } 193 194 // ProcessScript preprocess the script builtin function. 195 func ProcessScript(root *ast.File) error { 196 return sets.PreprocessBuiltinFunc(root, "script", func(values []ast.Node) (ast.Expr, error) { 197 for _, v := range values { 198 lit, ok := v.(*ast.BasicLit) 199 if ok { 200 src, err := literal.Unquote(lit.Value) 201 if err != nil { 202 return nil, errors.WithMessage(err, "unquote script value") 203 } 204 expr, err := parser.ParseExpr("-", src) 205 if err != nil { 206 return nil, errors.Errorf("script value(%s) is invalid CueLang", src) 207 } 208 return expr, nil 209 } 210 } 211 return nil, errors.New("script parameter error") 212 }) 213 } 214 215 type visitor struct { 216 r map[string]struct{} 217 } 218 219 func (vs *visitor) done(name string) { 220 vs.r[name] = struct{}{} 221 } 222 223 func (vs *visitor) shouldDo(name string) bool { 224 _, ok := vs.r[name] 225 return !ok 226 } 227 func (vs *visitor) addAttrForExpr(node ast.Node, index *int) { 228 switch v := node.(type) { 229 case *ast.Comprehension: 230 st := v.Value.(*ast.StructLit) 231 for _, elt := range st.Elts { 232 vs.addAttrForExpr(elt, index) 233 } 234 case *ast.Field: 235 basic, ok := v.Label.(*ast.Ident) 236 if !ok { 237 return 238 } 239 if !vs.shouldDo(basic.Name) { 240 return 241 } 242 if v.Attrs == nil { 243 *index++ 244 vs.done(basic.Name) 245 v.Attrs = []*ast.Attribute{ 246 {Text: fmt.Sprintf("@step(%d)", *index)}, 247 } 248 } 249 } 250 } 251 252 // MakeValue generate an value with same runtime 253 func (val *Value) MakeValue(s string) (*Value, error) { 254 builder := &build.Instance{} 255 file, err := parser.ParseFile("-", s, parser.ParseComments) 256 if err != nil { 257 return nil, err 258 } 259 if err := builder.AddSyntax(file); err != nil { 260 return nil, err 261 } 262 if err := val.addImports(builder); err != nil { 263 return nil, err 264 } 265 inst := val.r.BuildInstance(builder) 266 v := new(Value) 267 v.r = val.r 268 v.v = inst 269 v.addImports = val.addImports 270 if v.Error() != nil { 271 return nil, v.Error() 272 } 273 return v, nil 274 } 275 276 func (val *Value) makeValueWithFile(files ...*ast.File) (*Value, error) { 277 builder := &build.Instance{} 278 newFile := &ast.File{} 279 imports := map[string]*ast.ImportSpec{} 280 for _, f := range files { 281 for _, importSpec := range f.Imports { 282 if _, ok := imports[importSpec.Name.String()]; !ok { 283 imports[importSpec.Name.String()] = importSpec 284 } 285 } 286 newFile.Decls = append(newFile.Decls, f.Decls...) 287 } 288 289 for _, imp := range imports { 290 newFile.Imports = append(newFile.Imports, imp) 291 } 292 293 if err := builder.AddSyntax(newFile); err != nil { 294 return nil, err 295 } 296 if err := val.addImports(builder); err != nil { 297 return nil, err 298 } 299 inst := val.r.BuildInstance(builder) 300 v := new(Value) 301 v.r = val.r 302 v.v = inst 303 v.addImports = val.addImports 304 return v, nil 305 } 306 307 // FillRaw unify the value with the cue format string x at the given path. 308 func (val *Value) FillRaw(x string, paths ...string) error { 309 file, err := parser.ParseFile("-", x, parser.ParseComments) 310 if err != nil { 311 return err 312 } 313 xInst := val.r.BuildFile(file) 314 v := val.v.FillPath(FieldPath(paths...), xInst) 315 if v.Err() != nil { 316 return v.Err() 317 } 318 val.v = v 319 return nil 320 } 321 322 // FillValueByScript unify the value x at the given script path. 323 func (val *Value) FillValueByScript(x *Value, path string) error { 324 f, err := sets.OpenListLit(val.v) 325 if err != nil { 326 return err 327 } 328 v := val.r.BuildFile(f) 329 newV := v.FillPath(FieldPath(path), x.v) 330 if err := newV.Err(); err != nil { 331 return err 332 } 333 val.v = newV 334 return nil 335 } 336 337 func setValue(orig ast.Node, expr ast.Expr, selectors []cue.Selector) error { 338 if len(selectors) == 0 { 339 return nil 340 } 341 key := selectors[0] 342 selectors = selectors[1:] 343 switch x := orig.(type) { 344 case *ast.ListLit: 345 if key.Type() != cue.IndexLabel { 346 return fmt.Errorf("invalid key type %s in list lit", key.Type()) 347 } 348 if len(selectors) == 0 { 349 for key.Index() >= len(x.Elts) { 350 x.Elts = append(x.Elts, ast.NewStruct()) 351 } 352 x.Elts[key.Index()] = expr 353 return nil 354 } 355 return setValue(x.Elts[key.Index()], expr, selectors) 356 case *ast.StructLit: 357 if len(x.Elts) == 0 || (key.Type() == cue.StringLabel && len(sets.LookUpAll(x, key.String())) == 0) { 358 if len(selectors) == 0 { 359 x.Elts = append(x.Elts, &ast.Field{ 360 Label: ast.NewString(key.String()), 361 Value: expr, 362 }) 363 } else { 364 x.Elts = append(x.Elts, &ast.Field{ 365 Label: ast.NewString(key.String()), 366 Value: ast.NewStruct(), 367 }) 368 } 369 return setValue(x.Elts[len(x.Elts)-1].(*ast.Field).Value, expr, selectors) 370 } 371 for i := range x.Elts { 372 switch elem := x.Elts[i].(type) { 373 case *ast.Field: 374 if len(selectors) == 0 { 375 if key.Type() == cue.StringLabel && strings.Trim(sets.LabelStr(elem.Label), `"`) == strings.Trim(key.String(), `"`) { 376 x.Elts[i].(*ast.Field).Value = expr 377 return nil 378 } 379 } 380 if key.Type() == cue.StringLabel && strings.Trim(sets.LabelStr(elem.Label), `"`) == strings.Trim(key.String(), `"`) { 381 return setValue(x.Elts[i].(*ast.Field).Value, expr, selectors) 382 } 383 default: 384 return fmt.Errorf("not support type %T", elem) 385 } 386 } 387 default: 388 return fmt.Errorf("not support type %T", orig) 389 } 390 return nil 391 } 392 393 // SetValueByScript set the value v at the given script path. 394 // nolint:staticcheck 395 func (val *Value) SetValueByScript(v *Value, path ...string) error { 396 cuepath := FieldPath(path...) 397 selectors := cuepath.Selectors() 398 node := val.CueValue().Syntax(cue.ResolveReferences(true)) 399 if err := setValue(node, v.CueValue().Syntax(cue.ResolveReferences(true)).(ast.Expr), selectors); err != nil { 400 return err 401 } 402 b, err := format.Node(node) 403 if err != nil { 404 return err 405 } 406 val.v = val.r.CompileBytes(b) 407 return nil 408 } 409 410 // CueValue return cue.Value 411 func (val *Value) CueValue() cue.Value { 412 return val.v 413 } 414 415 // FillObject unify the value with object x at the given path. 416 func (val *Value) FillObject(x interface{}, paths ...string) error { 417 insert := x 418 if v, ok := x.(*Value); ok { 419 if v.r != val.r { 420 return errors.New("filled value not created with same Runtime") 421 } 422 insert = v.v 423 } 424 newV := val.v.FillPath(FieldPath(paths...), insert) 425 // do not check newV.Err() error here, because the value may be filled later 426 val.v = newV 427 return nil 428 } 429 430 // SetObject set the value with object x at the given path. 431 func (val *Value) SetObject(x interface{}, paths ...string) error { 432 insert := &Value{ 433 r: val.r, 434 } 435 switch v := x.(type) { 436 case *Value: 437 if v.r != val.r { 438 return errors.New("filled value not created with same Runtime") 439 } 440 insert.v = v.v 441 case ast.Expr: 442 cueV := val.r.BuildExpr(v) 443 insert.v = cueV 444 default: 445 return fmt.Errorf("not support type %T", x) 446 } 447 return val.SetValueByScript(insert, paths...) 448 } 449 450 // LookupValue reports the value at a path starting from val 451 func (val *Value) LookupValue(paths ...string) (*Value, error) { 452 v := val.v.LookupPath(FieldPath(paths...)) 453 if !v.Exists() { 454 return nil, errors.Errorf("failed to lookup value: var(path=%s) not exist", strings.Join(paths, ".")) 455 } 456 var field string 457 if len(paths) > 0 { 458 index := len(paths) - 1 459 if index < 0 { 460 index = 0 461 } 462 field = paths[index] 463 } 464 return &Value{ 465 v: v, 466 r: val.r, 467 field: field, 468 addImports: val.addImports, 469 }, nil 470 } 471 472 func isScript(content string) (bool, error) { 473 content = strings.TrimSpace(content) 474 scriptFile, err := parser.ParseFile("-", content, parser.ParseComments) 475 if err != nil { 476 return false, errors.WithMessage(err, "parse script") 477 } 478 if len(scriptFile.Imports) != 0 { 479 return true, nil 480 } 481 if len(scriptFile.Decls) == 0 || len(scriptFile.Decls) > 1 { 482 return true, nil 483 } 484 485 return !isSelector(scriptFile.Decls[0]), nil 486 } 487 488 func isSelector(node ast.Node) bool { 489 switch v := node.(type) { 490 case *ast.EmbedDecl: 491 return isSelector(v.Expr) 492 case *ast.SelectorExpr, *ast.IndexExpr, *ast.Ident: 493 return true 494 default: 495 return false 496 } 497 } 498 499 // LookupByScript reports the value by cue script. 500 func (val *Value) LookupByScript(script string) (*Value, error) { 501 var outputKey = "zz_output__" 502 script = strings.TrimSpace(script) 503 scriptFile, err := parser.ParseFile("-", script, parser.ParseComments) 504 if err != nil { 505 return nil, errors.WithMessage(err, "parse script") 506 } 507 isScriptPath, err := isScript(script) 508 if err != nil { 509 return nil, err 510 } 511 512 if !isScriptPath { 513 return val.LookupValue(script) 514 } 515 516 raw, err := val.String() 517 if err != nil { 518 return nil, err 519 } 520 521 rawFile, err := parser.ParseFile("-", raw, parser.ParseComments) 522 if err != nil { 523 return nil, errors.WithMessage(err, "parse script") 524 } 525 526 behindKey(scriptFile, outputKey) 527 528 newV, err := val.makeValueWithFile(rawFile, scriptFile) 529 if err != nil { 530 return nil, err 531 } 532 if newV.Error() != nil { 533 return nil, newV.Error() 534 } 535 536 return newV.LookupValue(outputKey) 537 } 538 539 func behindKey(file *ast.File, key string) { 540 var ( 541 implDecls []ast.Decl 542 decls []ast.Decl 543 ) 544 545 for i, decl := range file.Decls { 546 if _, ok := decl.(*ast.ImportDecl); ok { 547 implDecls = append(implDecls, file.Decls[i]) 548 } else { 549 decls = append(decls, file.Decls[i]) 550 } 551 } 552 553 file.Decls = implDecls 554 if len(decls) == 1 { 555 target := decls[0] 556 if embed, ok := target.(*ast.EmbedDecl); ok { 557 file.Decls = append(file.Decls, &ast.Field{ 558 Label: ast.NewIdent(key), 559 Value: embed.Expr, 560 }) 561 return 562 } 563 } 564 file.Decls = append(file.Decls, &ast.Field{ 565 Label: ast.NewIdent(key), 566 Value: &ast.StructLit{ 567 Elts: decls, 568 }, 569 }) 570 571 } 572 573 type field struct { 574 Name string 575 Value *Value 576 no int64 577 } 578 579 // StepByList process item in list. 580 func (val *Value) StepByList(handle func(name string, in *Value) (bool, error)) error { 581 iter, err := val.CueValue().List() 582 if err != nil { 583 return err 584 } 585 for iter.Next() { 586 stop, err := handle(iter.Label(), &Value{ 587 v: iter.Value(), 588 r: val.r, 589 field: iter.Label(), 590 addImports: val.addImports, 591 }) 592 if err != nil { 593 return err 594 } 595 if stop { 596 return nil 597 } 598 } 599 return nil 600 } 601 602 // StepByFields process the fields in order 603 func (val *Value) StepByFields(handle func(name string, in *Value) (bool, error)) error { 604 iter := steps(val) 605 for iter.next() { 606 iter.do(handle) 607 } 608 return iter.err 609 } 610 611 type stepsIterator struct { 612 queue []*field 613 index int 614 target *Value 615 err error 616 stopped bool 617 } 618 619 func steps(v *Value) *stepsIterator { 620 return &stepsIterator{ 621 target: v, 622 } 623 } 624 625 func (iter *stepsIterator) next() bool { 626 if iter.stopped { 627 return false 628 } 629 if iter.err != nil { 630 return false 631 } 632 if iter.queue != nil { 633 iter.index++ 634 } 635 iter.assemble() 636 return iter.index <= len(iter.queue)-1 637 } 638 639 func (iter *stepsIterator) assemble() { 640 filters := map[string]struct{}{} 641 for _, item := range iter.queue { 642 filters[item.Name] = struct{}{} 643 } 644 cueIter, err := iter.target.v.Fields(cue.Definitions(true), cue.Hidden(true), cue.All()) 645 if err != nil { 646 iter.err = err 647 return 648 } 649 var addFields []*field 650 for cueIter.Next() { 651 val := cueIter.Value() 652 name := cueIter.Label() 653 if val.IncompleteKind() == cue.TopKind { 654 continue 655 } 656 attr := val.Attribute("step") 657 no, err := attr.Int(0) 658 if err != nil { 659 no = 100 660 if name == "#do" || name == "#provider" { 661 no = 0 662 } 663 } 664 if _, ok := filters[name]; !ok { 665 addFields = append(addFields, &field{ 666 Name: name, 667 no: no, 668 }) 669 } 670 } 671 672 suffixItems := addFields 673 suffixItems = append(suffixItems, iter.queue[iter.index:]...) 674 sort.Sort(sortFields(suffixItems)) 675 iter.queue = append(iter.queue[:iter.index], suffixItems...) 676 } 677 678 func (iter *stepsIterator) value() *Value { 679 v := iter.target.v.LookupPath(FieldPath(iter.name())) 680 return &Value{ 681 r: iter.target.r, 682 v: v, 683 field: iter.name(), 684 addImports: iter.target.addImports, 685 } 686 } 687 688 func (iter *stepsIterator) name() string { 689 return iter.queue[iter.index].Name 690 } 691 692 func (iter *stepsIterator) do(handle func(name string, in *Value) (bool, error)) { 693 if iter.err != nil { 694 return 695 } 696 v := iter.value() 697 v.field = iter.name() 698 stopped, err := handle(iter.name(), v) 699 if err != nil { 700 iter.err = err 701 return 702 } 703 iter.stopped = stopped 704 if !isDef(iter.name()) { 705 if err := iter.target.FillObject(v, iter.name()); err != nil { 706 iter.err = err 707 return 708 } 709 } 710 } 711 712 type sortFields []*field 713 714 func (sf sortFields) Len() int { 715 return len(sf) 716 } 717 func (sf sortFields) Less(i, j int) bool { 718 return sf[i].no < sf[j].no 719 } 720 721 func (sf sortFields) Swap(i, j int) { 722 sf[i], sf[j] = sf[j], sf[i] 723 } 724 725 // Field return the cue value corresponding to the specified field 726 func (val *Value) Field(label string) (cue.Value, error) { 727 v := val.v.LookupPath(cue.ParsePath(label)) 728 if !v.Exists() { 729 return v, errors.Errorf("label %s not found", label) 730 } 731 732 if v.IncompleteKind() == cue.BottomKind { 733 return v, errors.Errorf("label %s's value not computed", label) 734 } 735 return v, nil 736 } 737 738 // GetString get the string value at a path starting from v. 739 func (val *Value) GetString(paths ...string) (string, error) { 740 v, err := val.LookupValue(paths...) 741 if err != nil { 742 return "", err 743 } 744 return v.CueValue().String() 745 } 746 747 // GetStringSlice get string slice from val 748 func (val *Value) GetStringSlice(paths ...string) ([]string, error) { 749 v, err := val.LookupValue(paths...) 750 if err != nil { 751 return nil, err 752 } 753 var s []string 754 err = v.UnmarshalTo(&s) 755 return s, err 756 } 757 758 // GetInt64 get the int value at a path starting from v. 759 func (val *Value) GetInt64(paths ...string) (int64, error) { 760 v, err := val.LookupValue(paths...) 761 if err != nil { 762 return 0, err 763 } 764 return v.CueValue().Int64() 765 } 766 767 // GetBool get the int value at a path starting from v. 768 func (val *Value) GetBool(paths ...string) (bool, error) { 769 v, err := val.LookupValue(paths...) 770 if err != nil { 771 return false, err 772 } 773 return v.CueValue().Bool() 774 } 775 776 // OpenCompleteValue make that the complete value can be modified. 777 func (val *Value) OpenCompleteValue() error { 778 newS, err := sets.OpenBaiscLit(val.CueValue()) 779 if err != nil { 780 return err 781 } 782 783 v := cuecontext.New().BuildFile(newS) 784 val.v = v 785 return nil 786 } 787 func isDef(s string) bool { 788 return strings.HasPrefix(s, "#") 789 } 790 791 // makePath creates a Path from a sequence of string. 792 func makePath(paths ...string) string { 793 mergedPath := "" 794 if len(paths) == 0 { 795 return mergedPath 796 } 797 mergedPath = paths[0] 798 if mergedPath == "" || (len(paths) == 1 && (strings.Contains(mergedPath, ".") || strings.Contains(mergedPath, "[") || isNumber(mergedPath))) { 799 return unquoteString(paths[0]) 800 } 801 if !strings.HasPrefix(mergedPath, "_") && !strings.HasPrefix(mergedPath, "#") { 802 mergedPath = fmt.Sprintf("\"%s\"", unquoteString(mergedPath)) 803 } 804 for _, p := range paths[1:] { 805 p = unquoteString(p) 806 if !strings.HasPrefix(p, "#") { 807 mergedPath += fmt.Sprintf("[\"%s\"]", p) 808 } else { 809 mergedPath += fmt.Sprintf(".%s", p) 810 } 811 } 812 return mergedPath 813 } 814 815 func unquoteString(s string) string { 816 if unquote, err := strconv.Unquote(s); err == nil { 817 return unquote 818 } 819 return s 820 } 821 822 func isNumber(s string) bool { 823 _, err := strconv.ParseInt(s, 10, 64) 824 return err == nil 825 } 826 827 // FieldPath return the cue path of the given paths 828 func FieldPath(paths ...string) cue.Path { 829 s := makePath(paths...) 830 if isNumber(s) { 831 return cue.MakePath(cue.Str(s)) 832 } 833 return cue.ParsePath(s) 834 }