Bump cel-go to v0.12.0

This commit is contained in:
Cici Huang
2022-07-07 17:13:57 +00:00
committed by cici37
parent aebe28b5cf
commit 772a252b06
131 changed files with 3842 additions and 1769 deletions

View File

@@ -31,6 +31,9 @@ type Error struct {
const (
dot = "."
ind = "^"
// maxSnippetLength is the largest number of characters which can be rendered in an error message snippet.
maxSnippetLength = 16384
)
var (
@@ -45,7 +48,7 @@ func (e *Error) ToDisplayString(source Source) string {
e.Location.Line(),
e.Location.Column()+1, // add one to the 0-based column for display
e.Message)
if snippet, found := source.Snippet(e.Location.Line()); found {
if snippet, found := source.Snippet(e.Location.Line()); found && len(snippet) <= maxSnippetLength {
snippet := strings.Replace(snippet, "\t", " ", -1)
srcLine := "\n | " + snippet
var bytes = []byte(snippet)

View File

@@ -17,23 +17,32 @@ package common
import (
"fmt"
"sort"
"strings"
)
// Errors type which contains a list of errors observed during parsing.
type Errors struct {
errors []Error
source Source
errors []Error
source Source
numErrors int
maxErrorsToReport int
}
// NewErrors creates a new instance of the Errors type.
func NewErrors(source Source) *Errors {
return &Errors{
errors: []Error{},
source: source}
errors: []Error{},
source: source,
maxErrorsToReport: 100,
}
}
// ReportError records an error at a source location.
func (e *Errors) ReportError(l Location, format string, args ...interface{}) {
e.numErrors++
if e.numErrors > e.maxErrorsToReport {
return
}
err := Error{
Location: l,
Message: fmt.Sprintf(format, args...),
@@ -46,18 +55,28 @@ func (e *Errors) GetErrors() []Error {
return e.errors[:]
}
// Append takes an Errors object as input creates a new Errors object with the current and input
// errors.
// Append creates a new Errors object with the current and input errors.
func (e *Errors) Append(errs []Error) *Errors {
return &Errors{
errors: append(e.errors, errs...),
source: e.source,
errors: append(e.errors, errs...),
source: e.source,
numErrors: e.numErrors + len(errs),
maxErrorsToReport: e.maxErrorsToReport,
}
}
// ToDisplayString returns the error set to a newline delimited string.
func (e *Errors) ToDisplayString() string {
var result = ""
errorsInString := e.maxErrorsToReport
if e.numErrors > e.maxErrorsToReport {
// add one more error to indicate the number of errors truncated.
errorsInString++
} else {
// otherwise the error set will just contain the number of errors.
errorsInString = e.numErrors
}
result := make([]string, errorsInString)
sort.SliceStable(e.errors, func(i, j int) bool {
ei := e.errors[i].Location
ej := e.errors[j].Location
@@ -65,10 +84,14 @@ func (e *Errors) ToDisplayString() string {
(ei.Line() == ej.Line() && ei.Column() < ej.Column())
})
for i, err := range e.errors {
if i >= 1 {
result += "\n"
// This can happen during the append of two errors objects
if i >= e.maxErrorsToReport {
break
}
result += err.ToDisplayString(e.source)
result[i] = err.ToDisplayString(e.source)
}
return result
if e.numErrors > e.maxErrorsToReport {
result[e.maxErrorsToReport] = fmt.Sprintf("%d more errors were truncated", e.numErrors-e.maxErrorsToReport)
}
return strings.Join(result, "\n")
}

View File

@@ -131,9 +131,11 @@ func (b Bool) Value() interface{} {
// IsBool returns whether the input ref.Val or ref.Type is equal to BoolType.
func IsBool(elem ref.Val) bool {
switch elem.(type) {
switch v := elem.(type) {
case Bool:
return true
case ref.Val:
return v.Type() == BoolType
default:
return false
}

View File

@@ -72,16 +72,13 @@ func MaybeNoSuchOverloadErr(val ref.Val) ref.Val {
return ValOrErr(val, "no such overload")
}
// ValOrErr either returns the existing error or create a new one.
// ValOrErr either returns the existing error or creates a new one.
// TODO: Audit the use of this function and standardize the error messages and codes.
func ValOrErr(val ref.Val, format string, args ...interface{}) ref.Val {
if val == nil {
if val == nil || !IsUnknownOrError(val) {
return NewErr(format, args...)
}
if IsUnknownOrError(val) {
return val
}
return NewErr(format, args...)
return val
}
// wrapErr wraps an existing Go error value into a CEL Err value.

View File

@@ -53,24 +53,24 @@ func NewObject(adapter ref.TypeAdapter,
}
func (o *protoObj) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
pb := o.value
if reflect.TypeOf(pb).AssignableTo(typeDesc) {
return pb, nil
srcPB := o.value
if reflect.TypeOf(srcPB).AssignableTo(typeDesc) {
return srcPB, nil
}
if reflect.TypeOf(o).AssignableTo(typeDesc) {
return o, nil
}
switch typeDesc {
case anyValueType:
_, isAny := pb.(*anypb.Any)
_, isAny := srcPB.(*anypb.Any)
if isAny {
return pb, nil
return srcPB, nil
}
return anypb.New(pb)
return anypb.New(srcPB)
case jsonValueType:
// Marshal the proto to JSON first, and then rehydrate as protobuf.Value as there is no
// support for direct conversion from proto.Message to protobuf.Value.
bytes, err := protojson.Marshal(pb)
bytes, err := protojson.Marshal(srcPB)
if err != nil {
return nil, err
}
@@ -88,7 +88,10 @@ func (o *protoObj) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
val := reflect.New(typeDesc.Elem()).Interface()
dstPB, ok := val.(proto.Message)
if ok {
proto.Merge(dstPB, pb)
err := pb.Merge(dstPB, srcPB)
if err != nil {
return nil, fmt.Errorf("type conversion error: %v", err)
}
return dstPB, nil
}
}

View File

@@ -21,7 +21,8 @@ import (
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/anypb"
anypb "google.golang.org/protobuf/types/known/anypb"
)
// Equal returns whether two proto.Message instances are equal using the following criteria:

View File

@@ -17,6 +17,8 @@
package pb
import (
"fmt"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
@@ -48,6 +50,31 @@ var (
}
)
// Merge will copy the source proto message into the destination, or error if the merge cannot be completed.
//
// Unlike the proto.Merge, this method will fallback to proto.Marshal/Unmarshal of the two proto messages do not
// share the same instance of their type descriptor.
func Merge(dstPB, srcPB proto.Message) error {
src, dst := srcPB.ProtoReflect(), dstPB.ProtoReflect()
if src.Descriptor() == dst.Descriptor() {
proto.Merge(dstPB, srcPB)
return nil
}
if src.Descriptor().FullName() != dst.Descriptor().FullName() {
return fmt.Errorf("pb.Merge() arguments must be the same type. got: %v, %v",
dst.Descriptor().FullName(), src.Descriptor().FullName())
}
bytes, err := proto.Marshal(srcPB)
if err != nil {
return fmt.Errorf("pb.Merge(dstPB, srcPB) failed to marshal source proto: %v", err)
}
err = proto.Unmarshal(bytes, dstPB)
if err != nil {
return fmt.Errorf("pb.Merge(dstPB, srcPB) failed to unmarshal to dest proto: %v", err)
}
return nil
}
// NewDb creates a new `pb.Db` with an empty type name to file description map.
func NewDb() *Db {
pbdb := &Db{

View File

@@ -87,7 +87,7 @@ func (td *TypeDescription) FieldByName(name string) (*FieldDescription, bool) {
// MaybeUnwrap accepts a proto message as input and unwraps it to a primitive CEL type if possible.
//
// This method returns the unwrapped value and 'true', else the original value and 'false'.
func (td *TypeDescription) MaybeUnwrap(msg proto.Message) (interface{}, bool) {
func (td *TypeDescription) MaybeUnwrap(msg proto.Message) (interface{}, bool, error) {
return unwrap(td, msg)
}
@@ -118,7 +118,7 @@ func NewFieldDescription(fieldDesc protoreflect.FieldDescriptor) *FieldDescripti
switch fieldDesc.Kind() {
case protoreflect.EnumKind:
reflectType = reflectTypeOf(protoreflect.EnumNumber(0))
case protoreflect.MessageKind:
case protoreflect.GroupKind, protoreflect.MessageKind:
zeroMsg = dynamicpb.NewMessage(fieldDesc.Message())
reflectType = reflectTypeOf(zeroMsg)
default:
@@ -248,8 +248,8 @@ func (fd *FieldDescription) GetFrom(target interface{}) (interface{}, error) {
return &Map{Map: fv, KeyType: fd.KeyType, ValueType: fd.ValueType}, nil
case protoreflect.Message:
// Make sure to unwrap well-known protobuf types before returning.
unwrapped, _ := fd.MaybeUnwrapDynamic(fv)
return unwrapped, nil
unwrapped, _, err := fd.MaybeUnwrapDynamic(fv)
return unwrapped, err
default:
return fv, nil
}
@@ -267,7 +267,8 @@ func (fd *FieldDescription) IsMap() bool {
// IsMessage returns true if the field is of message type.
func (fd *FieldDescription) IsMessage() bool {
return fd.desc.Kind() == protoreflect.MessageKind
kind := fd.desc.Kind()
return kind == protoreflect.MessageKind || kind == protoreflect.GroupKind
}
// IsOneof returns true if the field is declared within a oneof block.
@@ -288,7 +289,7 @@ func (fd *FieldDescription) IsList() bool {
//
// This function returns the unwrapped value and 'true' on success, or the original value
// and 'false' otherwise.
func (fd *FieldDescription) MaybeUnwrapDynamic(msg protoreflect.Message) (interface{}, bool) {
func (fd *FieldDescription) MaybeUnwrapDynamic(msg protoreflect.Message) (interface{}, bool, error) {
return unwrapDynamic(fd, msg)
}
@@ -316,7 +317,7 @@ func (fd *FieldDescription) Zero() proto.Message {
}
func (fd *FieldDescription) typeDefToType() *exprpb.Type {
if fd.desc.Kind() == protoreflect.MessageKind {
if fd.desc.Kind() == protoreflect.MessageKind || fd.desc.Kind() == protoreflect.GroupKind {
msgType := string(fd.desc.Message().FullName())
if wk, found := CheckedWellKnowns[msgType]; found {
return wk
@@ -361,63 +362,63 @@ func checkedWrap(t *exprpb.Type) *exprpb.Type {
// input message is a *dynamicpb.Message which obscures the typing information from Go.
//
// Returns the unwrapped value and 'true' if unwrapped, otherwise the input value and 'false'.
func unwrap(desc description, msg proto.Message) (interface{}, bool) {
func unwrap(desc description, msg proto.Message) (interface{}, bool, error) {
switch v := msg.(type) {
case *anypb.Any:
dynMsg, err := v.UnmarshalNew()
if err != nil {
return v, false
return v, false, err
}
return unwrapDynamic(desc, dynMsg.ProtoReflect())
case *dynamicpb.Message:
return unwrapDynamic(desc, v)
case *dpb.Duration:
return v.AsDuration(), true
return v.AsDuration(), true, nil
case *tpb.Timestamp:
return v.AsTime(), true
return v.AsTime(), true, nil
case *structpb.Value:
switch v.GetKind().(type) {
case *structpb.Value_BoolValue:
return v.GetBoolValue(), true
return v.GetBoolValue(), true, nil
case *structpb.Value_ListValue:
return v.GetListValue(), true
return v.GetListValue(), true, nil
case *structpb.Value_NullValue:
return structpb.NullValue_NULL_VALUE, true
return structpb.NullValue_NULL_VALUE, true, nil
case *structpb.Value_NumberValue:
return v.GetNumberValue(), true
return v.GetNumberValue(), true, nil
case *structpb.Value_StringValue:
return v.GetStringValue(), true
return v.GetStringValue(), true, nil
case *structpb.Value_StructValue:
return v.GetStructValue(), true
return v.GetStructValue(), true, nil
default:
return structpb.NullValue_NULL_VALUE, true
return structpb.NullValue_NULL_VALUE, true, nil
}
case *wrapperspb.BoolValue:
return v.GetValue(), true
return v.GetValue(), true, nil
case *wrapperspb.BytesValue:
return v.GetValue(), true
return v.GetValue(), true, nil
case *wrapperspb.DoubleValue:
return v.GetValue(), true
return v.GetValue(), true, nil
case *wrapperspb.FloatValue:
return float64(v.GetValue()), true
return float64(v.GetValue()), true, nil
case *wrapperspb.Int32Value:
return int64(v.GetValue()), true
return int64(v.GetValue()), true, nil
case *wrapperspb.Int64Value:
return v.GetValue(), true
return v.GetValue(), true, nil
case *wrapperspb.StringValue:
return v.GetValue(), true
return v.GetValue(), true, nil
case *wrapperspb.UInt32Value:
return uint64(v.GetValue()), true
return uint64(v.GetValue()), true, nil
case *wrapperspb.UInt64Value:
return v.GetValue(), true
return v.GetValue(), true, nil
}
return msg, false
return msg, false, nil
}
// unwrapDynamic unwraps a reflected protobuf Message value.
//
// Returns the unwrapped value and 'true' if unwrapped, otherwise the input value and 'false'.
func unwrapDynamic(desc description, refMsg protoreflect.Message) (interface{}, bool) {
func unwrapDynamic(desc description, refMsg protoreflect.Message) (interface{}, bool, error) {
msg := refMsg.Interface()
if !refMsg.IsValid() {
msg = desc.Zero()
@@ -432,18 +433,22 @@ func unwrapDynamic(desc description, refMsg protoreflect.Message) (interface{},
// unwrapped before being returned to the caller. Otherwise, the dynamic protobuf object
// represented by the Any will be returned.
unwrappedAny := &anypb.Any{}
proto.Merge(unwrappedAny, msg)
err := Merge(unwrappedAny, msg)
if err != nil {
return nil, false, err
}
dynMsg, err := unwrappedAny.UnmarshalNew()
if err != nil {
// Allow the error to move further up the stack as it should result in an type
// conversion error if the caller does not recover it somehow.
return unwrappedAny, true
return nil, false, err
}
// Attempt to unwrap the dynamic type, otherwise return the dynamic message.
if unwrapped, nested := unwrapDynamic(desc, dynMsg.ProtoReflect()); nested {
return unwrapped, true
unwrapped, nested, err := unwrapDynamic(desc, dynMsg.ProtoReflect())
if err == nil && nested {
return unwrapped, true, nil
}
return dynMsg, true
return dynMsg, true, err
case "google.protobuf.BoolValue",
"google.protobuf.BytesValue",
"google.protobuf.DoubleValue",
@@ -456,34 +461,49 @@ func unwrapDynamic(desc description, refMsg protoreflect.Message) (interface{},
// The msg value is ignored when dealing with wrapper types as they have a null or value
// behavior, rather than the standard zero value behavior of other proto message types.
if !refMsg.IsValid() {
return structpb.NullValue_NULL_VALUE, true
return structpb.NullValue_NULL_VALUE, true, nil
}
valueField := refMsg.Descriptor().Fields().ByName("value")
return refMsg.Get(valueField).Interface(), true
return refMsg.Get(valueField).Interface(), true, nil
case "google.protobuf.Duration":
unwrapped := &dpb.Duration{}
proto.Merge(unwrapped, msg)
return unwrapped.AsDuration(), true
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrapped.AsDuration(), true, nil
case "google.protobuf.ListValue":
unwrapped := &structpb.ListValue{}
proto.Merge(unwrapped, msg)
return unwrapped, true
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrapped, true, nil
case "google.protobuf.NullValue":
return structpb.NullValue_NULL_VALUE, true
return structpb.NullValue_NULL_VALUE, true, nil
case "google.protobuf.Struct":
unwrapped := &structpb.Struct{}
proto.Merge(unwrapped, msg)
return unwrapped, true
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrapped, true, nil
case "google.protobuf.Timestamp":
unwrapped := &tpb.Timestamp{}
proto.Merge(unwrapped, msg)
return unwrapped.AsTime(), true
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrapped.AsTime(), true, nil
case "google.protobuf.Value":
unwrapped := &structpb.Value{}
proto.Merge(unwrapped, msg)
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrap(desc, unwrapped)
}
return msg, false
return msg, false, nil
}
// reflectTypeOf intercepts the reflect.Type call to ensure that dynamicpb.Message types preserve

View File

@@ -206,7 +206,10 @@ func (p *protoTypeRegistry) NativeToValue(value interface{}) ref.Val {
if !found {
return NewErr("unknown type: '%s'", typeName)
}
unwrapped, isUnwrapped := td.MaybeUnwrap(v)
unwrapped, isUnwrapped, err := td.MaybeUnwrap(v)
if err != nil {
return UnsupportedRefValConversionErr(v)
}
if isUnwrapped {
return p.NativeToValue(unwrapped)
}
@@ -394,7 +397,10 @@ func nativeToValue(a ref.TypeAdapter, value interface{}) (ref.Val, bool) {
if !found {
return nil, false
}
val, unwrapped := td.MaybeUnwrap(v)
val, unwrapped, err := td.MaybeUnwrap(v)
if err != nil {
return UnsupportedRefValConversionErr(v), true
}
if !unwrapped {
return nil, false
}

View File

@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package ref contains the reference interfaces used throughout the types
// components.
// Package ref contains the reference interfaces used throughout the types components.
package ref
import (
@@ -29,31 +28,27 @@ type Type interface {
// TypeName returns the qualified type name of the type.
//
// The type name is also used as the type's identifier name at type-check
// and interpretation time.
// The type name is also used as the type's identifier name at type-check and interpretation time.
TypeName() string
}
// Val interface defines the functions supported by all expression values.
// Val implementations may specialize the behavior of the value through the
// addition of traits.
// Val implementations may specialize the behavior of the value through the addition of traits.
type Val interface {
// ConvertToNative converts the Value to a native Go struct according to the
// reflected type description, or error if the conversion is not feasible.
ConvertToNative(typeDesc reflect.Type) (interface{}, error)
// ConvertToType supports type conversions between value types supported by
// the expression language.
// ConvertToType supports type conversions between value types supported by the expression language.
ConvertToType(typeValue Type) Val
// Equal returns true if the `other` value has the same type and content as
// the implementing struct.
// Equal returns true if the `other` value has the same type and content as the implementing struct.
Equal(other Val) Val
// Type returns the TypeValue of the value.
Type() Type
// Value returns the raw value of the instance which may not be directly
// compatible with the expression language types.
// Value returns the raw value of the instance which may not be directly compatible with the expression
// language types.
Value() interface{}
}

View File

@@ -299,7 +299,7 @@ func timeZone(tz ref.Val, visitor timestampVisitor) timestampVisitor {
if err != nil {
return wrapErr(err)
}
min, err := strconv.Atoi(string(val[ind+1]))
min, err := strconv.Atoi(string(val[ind+1:]))
if err != nil {
return wrapErr(err)
}