github.com/oam-dev/kubevela@v1.9.11/pkg/cue/convert.go (about)

     1  /*
     2  Copyright 2021 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 cue
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"cuelang.org/go/cue"
    25  
    26  	"github.com/kubevela/workflow/pkg/cue/model/value"
    27  	"github.com/kubevela/workflow/pkg/cue/packages"
    28  
    29  	"github.com/oam-dev/kubevela/apis/types"
    30  	"github.com/oam-dev/kubevela/pkg/cue/process"
    31  )
    32  
    33  // ErrParameterNotExist represents the parameter field is not exist in CUE template
    34  var ErrParameterNotExist = errors.New("parameter not exist")
    35  
    36  // GetParameters get parameter from cue template
    37  func GetParameters(templateStr string, pd *packages.PackageDiscover) ([]types.Parameter, error) {
    38  	template, err := value.NewValue(templateStr+BaseTemplate, pd, "")
    39  	if err != nil {
    40  		return nil, fmt.Errorf("error in template: %w", err)
    41  	}
    42  	paramVal, err := template.LookupValue(process.ParameterFieldName)
    43  	if err != nil || !paramVal.CueValue().Exists() {
    44  		return nil, ErrParameterNotExist
    45  	}
    46  	iter, err := paramVal.CueValue().Fields(cue.Definitions(true), cue.Hidden(true), cue.All())
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	// parse each fields in the parameter fields
    51  	var params []types.Parameter
    52  	for iter.Next() {
    53  		if iter.Selector().IsDefinition() {
    54  			continue
    55  		}
    56  		var param = types.Parameter{
    57  			Name:     iter.Label(),
    58  			Required: !iter.IsOptional(),
    59  		}
    60  		val := iter.Value()
    61  		param.Type = val.IncompleteKind()
    62  		if def, ok := val.Default(); ok && def.IsConcrete() {
    63  			param.Required = false
    64  			param.Type = def.Kind()
    65  			param.Default = GetDefault(def)
    66  		}
    67  		if param.Default == nil {
    68  			param.Default = getDefaultByKind(param.Type)
    69  		}
    70  		param.Short, param.Usage, param.Alias, param.Ignore = RetrieveComments(val)
    71  
    72  		params = append(params, param)
    73  	}
    74  	return params, nil
    75  }
    76  
    77  func getDefaultByKind(k cue.Kind) interface{} {
    78  	// nolint:exhaustive
    79  	switch k {
    80  	case cue.IntKind:
    81  		var d int64
    82  		return d
    83  	case cue.StringKind:
    84  		var d string
    85  		return d
    86  	case cue.BoolKind:
    87  		var d bool
    88  		return d
    89  	case cue.NumberKind, cue.FloatKind:
    90  		var d float64
    91  		return d
    92  	default:
    93  		// assume other cue kind won't be valid parameter
    94  	}
    95  	return nil
    96  }
    97  
    98  // GetDefault evaluate default Go value from CUE
    99  func GetDefault(val cue.Value) interface{} {
   100  	// nolint:exhaustive
   101  	switch val.Kind() {
   102  	case cue.IntKind:
   103  		if d, err := val.Int64(); err == nil {
   104  			return d
   105  		}
   106  	case cue.StringKind:
   107  		if d, err := val.String(); err == nil {
   108  			return d
   109  		}
   110  	case cue.BoolKind:
   111  		if d, err := val.Bool(); err == nil {
   112  			return d
   113  		}
   114  	case cue.NumberKind, cue.FloatKind:
   115  		if d, err := val.Float64(); err == nil {
   116  			return d
   117  		}
   118  	default:
   119  	}
   120  	return getDefaultByKind(val.Kind())
   121  }
   122  
   123  const (
   124  	// UsagePrefix defines the usage display for KubeVela CLI
   125  	UsagePrefix = "+usage="
   126  	// ShortPrefix defines the short argument for KubeVela CLI
   127  	ShortPrefix = "+short="
   128  	// AliasPrefix is an alias of the name of a parameter element, in order to making it more friendly to Cli users
   129  	AliasPrefix = "+alias="
   130  	// IgnorePrefix defines parameter in system level which we don't want our end user to see for KubeVela CLI
   131  	IgnorePrefix = "+ignore"
   132  )
   133  
   134  // RetrieveComments will retrieve Usage, Short, Alias and Ignore from CUE Value
   135  func RetrieveComments(value cue.Value) (string, string, string, bool) {
   136  	var short, usage, alias string
   137  	var ignore bool
   138  	docs := value.Doc()
   139  	for _, doc := range docs {
   140  		lines := strings.Split(doc.Text(), "\n")
   141  		for _, line := range lines {
   142  			line = strings.TrimSpace(line)
   143  			line = strings.TrimPrefix(line, "//")
   144  			line = strings.TrimSpace(line)
   145  			if strings.HasPrefix(line, ShortPrefix) {
   146  				short = strings.TrimPrefix(line, ShortPrefix)
   147  			}
   148  			if strings.HasPrefix(line, IgnorePrefix) {
   149  				ignore = true
   150  			}
   151  			if strings.HasPrefix(line, UsagePrefix) {
   152  				usage = strings.TrimPrefix(line, UsagePrefix)
   153  			}
   154  			if strings.HasPrefix(line, AliasPrefix) {
   155  				alias = strings.TrimPrefix(line, AliasPrefix)
   156  			}
   157  		}
   158  	}
   159  	return short, usage, alias, ignore
   160  }
   161  
   162  // IsFieldNotExist check whether the error type is the field not found
   163  func IsFieldNotExist(err error) bool {
   164  	return strings.Contains(err.Error(), "not exist")
   165  }