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 }