lib/send.go (115 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package lib import ( "fmt" "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" ) // Send returns a cel.EnvOption to configure extended functions for sending // values on a Go channel during expression evaluation. The channel should be // received from by a goroutine running in the host program. Send to calls // will allow error values to be passed as arguments in the <dyn> position. // // # Send ref.Val To // // Sends a value as a ref.Val to the named channel and returns the value: // // <dyn>.send_to(<string>) -> <dyn> // send_to(<dyn>, <string>) -> <dyn> // // # Send To // // Sends a value to the named channel and returns the value: // // <dyn>.send_to(<string>) -> <dyn> // send_to(<dyn>, <string>) -> <dyn> // // # Close // // Closes the named channel and returns true. It will cause an error if the // same name is closed more than once in an expression. The dyn received is // ignored. // // <dyn>.close(<string>) -> <bool> func Send(ch map[string]chan interface{}) cel.EnvOption { return cel.Lib(sendLib(ch)) } type sendLib map[string]chan interface{} func (ch sendLib) CompileOptions() []cel.EnvOption { return []cel.EnvOption{ cel.Function("send_refval_to", cel.MemberOverload( "dyn_send_refval_string", []*cel.Type{cel.DynType, cel.StringType}, cel.DynType, cel.BinaryBinding(ch.sendRefVal), cel.OverloadIsNonStrict(), ), cel.Overload( "send_dyn_refval_string", []*cel.Type{cel.DynType, cel.StringType}, cel.DynType, cel.BinaryBinding(ch.sendRefVal), cel.OverloadIsNonStrict(), ), ), cel.Function("send_to", cel.MemberOverload( "dyn_send_string", []*cel.Type{cel.DynType, cel.StringType}, cel.DynType, cel.BinaryBinding(ch.send), cel.OverloadIsNonStrict(), ), cel.Overload( "send_dyn_string", []*cel.Type{cel.DynType, cel.StringType}, cel.DynType, cel.BinaryBinding(ch.send), cel.OverloadIsNonStrict(), ), ), cel.Function("close", cel.MemberOverload( "dyn_close_string", []*cel.Type{cel.DynType, cel.StringType}, cel.BoolType, cel.BinaryBinding(ch.close), ), ), } } func (sendLib) ProgramOptions() []cel.ProgramOption { return nil } func (ch sendLib) close(_, arg ref.Val) ref.Val { name, ok := arg.(types.String) if !ok { return types.NoSuchOverloadErr() } c, ok := ch[string(name)] if !ok { return types.NewErr("no channel %s", name) } close(c) return types.Bool(true) } func (ch sendLib) sendRefVal(val, arg ref.Val) ref.Val { name, ok := arg.(types.String) if !ok { return types.NoSuchOverloadErr() } c, ok := ch[string(name)] if !ok { return types.NewErr("no channel %s", name) } c <- val return val } func (ch sendLib) send(val, arg ref.Val) ref.Val { name, ok := arg.(types.String) if !ok { return types.NoSuchOverloadErr() } c, ok := ch[string(name)] if !ok { return types.NewErr("no channel %s", name) } var ( v interface{} err error ) typ, ok := encodableTypes[val.Type()] if ok { v, err = val.ConvertToNative(typ) if err != nil { // This should never happen. panic(fmt.Sprintf("type mapping out of sync: %v", err)) } } else { for _, typ := range protobufTypes { v, err = val.ConvertToNative(typ) if err != nil { v = nil } else { break } } } if v == nil { return types.NewErr("failed to get native value to send") } c <- v return val }