@@ -16,6 +16,8 @@ package cel
16
16
17
17
import (
18
18
"errors"
19
+ "fmt"
20
+ "math"
19
21
"sync"
20
22
21
23
"github.com/google/cel-go/checker"
@@ -24,12 +26,15 @@ import (
24
26
celast "github.com/google/cel-go/common/ast"
25
27
"github.com/google/cel-go/common/containers"
26
28
"github.com/google/cel-go/common/decls"
29
+ "github.com/google/cel-go/common/env"
30
+ "github.com/google/cel-go/common/stdlib"
27
31
"github.com/google/cel-go/common/types"
28
32
"github.com/google/cel-go/common/types/ref"
29
33
"github.com/google/cel-go/interpreter"
30
34
"github.com/google/cel-go/parser"
31
35
32
36
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
37
+ "google.golang.org/protobuf/reflect/protoreflect"
33
38
)
34
39
35
40
// Source interface representing a user-provided expression.
@@ -127,12 +132,13 @@ type Env struct {
127
132
Container * containers.Container
128
133
variables []* decls.VariableDecl
129
134
functions map [string ]* decls.FunctionDecl
130
- macros []parser.Macro
135
+ macros []Macro
136
+ contextProto protoreflect.MessageDescriptor
131
137
adapter types.Adapter
132
138
provider types.Provider
133
139
features map [int ]bool
134
140
appliedFeatures map [int ]bool
135
- libraries map [string ]bool
141
+ libraries map [string ]SingletonLibrary
136
142
validators []ASTValidator
137
143
costOptions []checker.CostOption
138
144
@@ -151,6 +157,115 @@ type Env struct {
151
157
progOpts []ProgramOption
152
158
}
153
159
160
+ // ToConfig produces a YAML-serializable env.Config object from the given environment.
161
+ //
162
+ // The serialized configuration value is intended to represent a baseline set of config
163
+ // options which could be used as input to an EnvOption to configure the majority of the
164
+ // environment from a file.
165
+ //
166
+ // Note: validators, features, flags, and safe-guard settings are not yet supported by
167
+ // the serialize method. Since optimizers are a separate construct from the environment
168
+ // and the standard expression components (parse, check, evalute), they are also not
169
+ // supported by the serialize method.
170
+ func (e * Env ) ToConfig (name string ) (* env.Config , error ) {
171
+ conf := env .NewConfig (name )
172
+ // Container settings
173
+ if e .Container != containers .DefaultContainer {
174
+ conf .SetContainer (e .Container .Name ())
175
+ }
176
+ for _ , typeName := range e .Container .AliasSet () {
177
+ conf .AddImports (env .NewImport (typeName ))
178
+ }
179
+
180
+ libOverloads := map [string ][]string {}
181
+ for libName , lib := range e .libraries {
182
+ // Track the options which have been configured by a library and
183
+ // then diff the library version against the configured function
184
+ // to detect incremental overloads or rewrites.
185
+ libEnv , _ := NewCustomEnv ()
186
+ libEnv , _ = Lib (lib )(libEnv )
187
+ for fnName , fnDecl := range libEnv .Functions () {
188
+ if len (fnDecl .OverloadDecls ()) == 0 {
189
+ continue
190
+ }
191
+ overloads , exist := libOverloads [fnName ]
192
+ if ! exist {
193
+ overloads = make ([]string , 0 , len (fnDecl .OverloadDecls ()))
194
+ }
195
+ for _ , o := range fnDecl .OverloadDecls () {
196
+ overloads = append (overloads , o .ID ())
197
+ }
198
+ libOverloads [fnName ] = overloads
199
+ }
200
+ subsetLib , canSubset := lib .(LibrarySubsetter )
201
+ alias := ""
202
+ if aliasLib , canAlias := lib .(LibraryAliaser ); canAlias {
203
+ alias = aliasLib .LibraryAlias ()
204
+ libName = alias
205
+ }
206
+ if libName == "stdlib" && canSubset {
207
+ conf .SetStdLib (subsetLib .LibrarySubset ())
208
+ continue
209
+ }
210
+ version := uint32 (math .MaxUint32 )
211
+ if versionLib , isVersioned := lib .(LibraryVersioner ); isVersioned {
212
+ version = versionLib .LibraryVersion ()
213
+ }
214
+ conf .AddExtensions (env .NewExtension (libName , version ))
215
+ }
216
+
217
+ // If this is a custom environment without the standard env, mark the stdlib as disabled.
218
+ if conf .StdLib == nil && ! e .HasLibrary ("cel.lib.std" ) {
219
+ conf .SetStdLib (env .NewLibrarySubset ().SetDisabled (true ))
220
+ }
221
+
222
+ // Serialize the variables
223
+ vars := make ([]* decls.VariableDecl , 0 , len (e .Variables ()))
224
+ stdTypeVars := map [string ]* decls.VariableDecl {}
225
+ for _ , v := range stdlib .Types () {
226
+ stdTypeVars [v .Name ()] = v
227
+ }
228
+ for _ , v := range e .Variables () {
229
+ if _ , isStdType := stdTypeVars [v .Name ()]; isStdType {
230
+ continue
231
+ }
232
+ vars = append (vars , v )
233
+ }
234
+ if e .contextProto != nil {
235
+ conf .SetContextVariable (env .NewContextVariable (string (e .contextProto .FullName ())))
236
+ skipVariables := map [string ]bool {}
237
+ fields := e .contextProto .Fields ()
238
+ for i := 0 ; i < fields .Len (); i ++ {
239
+ field := fields .Get (i )
240
+ variable , err := fieldToVariable (field )
241
+ if err != nil {
242
+ return nil , fmt .Errorf ("could not serialize context field variable %q, reason: %w" , field .FullName (), err )
243
+ }
244
+ skipVariables [variable .Name ()] = true
245
+ }
246
+ for _ , v := range vars {
247
+ if _ , found := skipVariables [v .Name ()]; ! found {
248
+ conf .AddVariableDecls (v )
249
+ }
250
+ }
251
+ } else {
252
+ conf .AddVariableDecls (vars ... )
253
+ }
254
+
255
+ // Serialize functions which are distinct from the ones configured by libraries.
256
+ for fnName , fnDecl := range e .Functions () {
257
+ if excludedOverloads , found := libOverloads [fnName ]; found {
258
+ if newDecl := fnDecl .Subset (decls .ExcludeOverloads (excludedOverloads ... )); newDecl != nil {
259
+ conf .AddFunctionDecls (newDecl )
260
+ }
261
+ } else {
262
+ conf .AddFunctionDecls (fnDecl )
263
+ }
264
+ }
265
+
266
+ return conf , nil
267
+ }
268
+
154
269
// NewEnv creates a program environment configured with the standard library of CEL functions and
155
270
// macros. The Env value returned can parse and check any CEL program which builds upon the core
156
271
// features documented in the CEL specification.
@@ -194,7 +309,7 @@ func NewCustomEnv(opts ...EnvOption) (*Env, error) {
194
309
provider : registry ,
195
310
features : map [int ]bool {},
196
311
appliedFeatures : map [int ]bool {},
197
- libraries : map [string ]bool {},
312
+ libraries : map [string ]SingletonLibrary {},
198
313
validators : []ASTValidator {},
199
314
progOpts : []ProgramOption {},
200
315
costOptions : []checker.CostOption {},
@@ -362,7 +477,7 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
362
477
for k , v := range e .functions {
363
478
funcsCopy [k ] = v
364
479
}
365
- libsCopy := make (map [string ]bool , len (e .libraries ))
480
+ libsCopy := make (map [string ]SingletonLibrary , len (e .libraries ))
366
481
for k , v := range e .libraries {
367
482
libsCopy [k ] = v
368
483
}
@@ -376,6 +491,7 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
376
491
variables : varsCopy ,
377
492
functions : funcsCopy ,
378
493
macros : macsCopy ,
494
+ contextProto : e .contextProto ,
379
495
progOpts : progOptsCopy ,
380
496
adapter : adapter ,
381
497
features : featuresCopy ,
@@ -399,8 +515,8 @@ func (e *Env) HasFeature(flag int) bool {
399
515
400
516
// HasLibrary returns whether a specific SingletonLibrary has been configured in the environment.
401
517
func (e * Env ) HasLibrary (libName string ) bool {
402
- configured , exists := e .libraries [libName ]
403
- return exists && configured
518
+ _ , exists := e .libraries [libName ]
519
+ return exists
404
520
}
405
521
406
522
// Libraries returns a list of SingletonLibrary that have been configured in the environment.
@@ -423,6 +539,11 @@ func (e *Env) Functions() map[string]*decls.FunctionDecl {
423
539
return e .functions
424
540
}
425
541
542
+ // Variables returns the set of variables associated with the environment.
543
+ func (e * Env ) Variables () []* decls.VariableDecl {
544
+ return e .variables
545
+ }
546
+
426
547
// HasValidator returns whether a specific ASTValidator has been configured in the environment.
427
548
func (e * Env ) HasValidator (name string ) bool {
428
549
for _ , v := range e .validators {
0 commit comments