k8s.io/apiserver@v0.31.1/pkg/cel/library/regex.go (about) 1 /* 2 Copyright 2022 The Kubernetes 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 library 18 19 import ( 20 "regexp" 21 22 "github.com/google/cel-go/cel" 23 "github.com/google/cel-go/common/types" 24 "github.com/google/cel-go/common/types/ref" 25 "github.com/google/cel-go/interpreter" 26 ) 27 28 // Regex provides a CEL function library extension of regex utility functions. 29 // 30 // find / findAll 31 // 32 // Returns substrings that match the provided regular expression. find returns the first match. findAll may optionally 33 // be provided a limit. If the limit is set and >= 0, no more than the limit number of matches are returned. 34 // 35 // <string>.find(<string>) <string> 36 // <string>.findAll(<string>) <list <string>> 37 // <string>.findAll(<string>, <int>) <list <string>> 38 // 39 // Examples: 40 // 41 // "abc 123".find('[0-9]*') // returns '123' 42 // "abc 123".find('xyz') // returns '' 43 // "123 abc 456".findAll('[0-9]*') // returns ['123', '456'] 44 // "123 abc 456".findAll('[0-9]*', 1) // returns ['123'] 45 // "123 abc 456".findAll('xyz') // returns [] 46 func Regex() cel.EnvOption { 47 return cel.Lib(regexLib) 48 } 49 50 var regexLib = ®ex{} 51 52 type regex struct{} 53 54 func (*regex) LibraryName() string { 55 return "k8s.regex" 56 } 57 58 var regexLibraryDecls = map[string][]cel.FunctionOpt{ 59 "find": { 60 cel.MemberOverload("string_find_string", []*cel.Type{cel.StringType, cel.StringType}, cel.StringType, 61 cel.BinaryBinding(find))}, 62 "findAll": { 63 cel.MemberOverload("string_find_all_string", []*cel.Type{cel.StringType, cel.StringType}, 64 cel.ListType(cel.StringType), 65 cel.BinaryBinding(func(str, regex ref.Val) ref.Val { 66 return findAll(str, regex, types.Int(-1)) 67 })), 68 cel.MemberOverload("string_find_all_string_int", 69 []*cel.Type{cel.StringType, cel.StringType, cel.IntType}, 70 cel.ListType(cel.StringType), 71 cel.FunctionBinding(findAll)), 72 }, 73 } 74 75 func (*regex) CompileOptions() []cel.EnvOption { 76 options := []cel.EnvOption{} 77 for name, overloads := range regexLibraryDecls { 78 options = append(options, cel.Function(name, overloads...)) 79 } 80 return options 81 } 82 83 func (*regex) ProgramOptions() []cel.ProgramOption { 84 return []cel.ProgramOption{ 85 cel.OptimizeRegex(FindRegexOptimization, FindAllRegexOptimization), 86 } 87 } 88 89 func find(strVal ref.Val, regexVal ref.Val) ref.Val { 90 str, ok := strVal.Value().(string) 91 if !ok { 92 return types.MaybeNoSuchOverloadErr(strVal) 93 } 94 regex, ok := regexVal.Value().(string) 95 if !ok { 96 return types.MaybeNoSuchOverloadErr(regexVal) 97 } 98 re, err := regexp.Compile(regex) 99 if err != nil { 100 return types.NewErr("Illegal regex: %v", err.Error()) 101 } 102 result := re.FindString(str) 103 return types.String(result) 104 } 105 106 func findAll(args ...ref.Val) ref.Val { 107 argn := len(args) 108 if argn < 2 || argn > 3 { 109 return types.NoSuchOverloadErr() 110 } 111 str, ok := args[0].Value().(string) 112 if !ok { 113 return types.MaybeNoSuchOverloadErr(args[0]) 114 } 115 regex, ok := args[1].Value().(string) 116 if !ok { 117 return types.MaybeNoSuchOverloadErr(args[1]) 118 } 119 n := int64(-1) 120 if argn == 3 { 121 n, ok = args[2].Value().(int64) 122 if !ok { 123 return types.MaybeNoSuchOverloadErr(args[2]) 124 } 125 } 126 127 re, err := regexp.Compile(regex) 128 if err != nil { 129 return types.NewErr("Illegal regex: %v", err.Error()) 130 } 131 132 result := re.FindAllString(str, int(n)) 133 134 return types.NewStringList(types.DefaultTypeAdapter, result) 135 } 136 137 // FindRegexOptimization optimizes the 'find' function by compiling the regex pattern and 138 // reporting any compilation errors at program creation time, and using the compiled regex pattern for all function 139 // call invocations. 140 var FindRegexOptimization = &interpreter.RegexOptimization{ 141 Function: "find", 142 RegexIndex: 1, 143 Factory: func(call interpreter.InterpretableCall, regexPattern string) (interpreter.InterpretableCall, error) { 144 compiledRegex, err := regexp.Compile(regexPattern) 145 if err != nil { 146 return nil, err 147 } 148 return interpreter.NewCall(call.ID(), call.Function(), call.OverloadID(), call.Args(), func(args ...ref.Val) ref.Val { 149 if len(args) != 2 { 150 return types.NoSuchOverloadErr() 151 } 152 in, ok := args[0].Value().(string) 153 if !ok { 154 return types.MaybeNoSuchOverloadErr(args[0]) 155 } 156 return types.String(compiledRegex.FindString(in)) 157 }), nil 158 }, 159 } 160 161 // FindAllRegexOptimization optimizes the 'findAll' function by compiling the regex pattern and 162 // reporting any compilation errors at program creation time, and using the compiled regex pattern for all function 163 // call invocations. 164 var FindAllRegexOptimization = &interpreter.RegexOptimization{ 165 Function: "findAll", 166 RegexIndex: 1, 167 Factory: func(call interpreter.InterpretableCall, regexPattern string) (interpreter.InterpretableCall, error) { 168 compiledRegex, err := regexp.Compile(regexPattern) 169 if err != nil { 170 return nil, err 171 } 172 return interpreter.NewCall(call.ID(), call.Function(), call.OverloadID(), call.Args(), func(args ...ref.Val) ref.Val { 173 argn := len(args) 174 if argn < 2 || argn > 3 { 175 return types.NoSuchOverloadErr() 176 } 177 str, ok := args[0].Value().(string) 178 if !ok { 179 return types.MaybeNoSuchOverloadErr(args[0]) 180 } 181 n := int64(-1) 182 if argn == 3 { 183 n, ok = args[2].Value().(int64) 184 if !ok { 185 return types.MaybeNoSuchOverloadErr(args[2]) 186 } 187 } 188 189 result := compiledRegex.FindAllString(str, int(n)) 190 return types.NewStringList(types.DefaultTypeAdapter, result) 191 }), nil 192 }, 193 }