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  }