// Copyright 2023 Google LLC // // Licensed 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 checker import ( "fmt" "strings" chkdecls "github.com/google/cel-go/checker/decls" "github.com/google/cel-go/common/types" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) const ( kindUnknown = iota + 1 kindError kindFunction kindDyn kindPrimitive kindWellKnown kindWrapper kindNull kindAbstract kindType kindList kindMap kindObject kindTypeParam ) // FormatCheckedType converts a type message into a string representation. func FormatCheckedType(t *exprpb.Type) string { switch kindOf(t) { case kindDyn: return "dyn" case kindFunction: return formatFunctionExprType(t.GetFunction().GetResultType(), t.GetFunction().GetArgTypes(), false) case kindList: return fmt.Sprintf("list(%s)", FormatCheckedType(t.GetListType().GetElemType())) case kindObject: return t.GetMessageType() case kindMap: return fmt.Sprintf("map(%s, %s)", FormatCheckedType(t.GetMapType().GetKeyType()), FormatCheckedType(t.GetMapType().GetValueType())) case kindNull: return "null" case kindPrimitive: switch t.GetPrimitive() { case exprpb.Type_UINT64: return "uint" case exprpb.Type_INT64: return "int" } return strings.Trim(strings.ToLower(t.GetPrimitive().String()), " ") case kindType: if t.GetType() == nil || t.GetType().GetTypeKind() == nil { return "type" } return fmt.Sprintf("type(%s)", FormatCheckedType(t.GetType())) case kindWellKnown: switch t.GetWellKnown() { case exprpb.Type_ANY: return "any" case exprpb.Type_DURATION: return "duration" case exprpb.Type_TIMESTAMP: return "timestamp" } case kindWrapper: return fmt.Sprintf("wrapper(%s)", FormatCheckedType(chkdecls.NewPrimitiveType(t.GetWrapper()))) case kindError: return "!error!" case kindTypeParam: return t.GetTypeParam() case kindAbstract: at := t.GetAbstractType() params := at.GetParameterTypes() paramStrs := make([]string, len(params)) for i, p := range params { paramStrs[i] = FormatCheckedType(p) } return fmt.Sprintf("%s(%s)", at.GetName(), strings.Join(paramStrs, ", ")) } return t.String() } type formatter func(any) string // FormatCELType formats a types.Type value to a string representation. // // The type formatting is identical to FormatCheckedType. func FormatCELType(t any) string { dt := t.(*types.Type) switch dt.Kind() { case types.AnyKind: return "any" case types.DurationKind: return "duration" case types.ErrorKind: return "!error!" case types.NullTypeKind: return "null" case types.TimestampKind: return "timestamp" case types.TypeParamKind: return dt.TypeName() case types.OpaqueKind: if dt.TypeName() == "function" { // There is no explicit function type in the new types representation, so information like // whether the function is a member function is absent. return formatFunctionDeclType(dt.Parameters()[0], dt.Parameters()[1:], false) } case types.UnspecifiedKind: return "" } if len(dt.Parameters()) == 0 { return dt.DeclaredTypeName() } paramTypeNames := make([]string, 0, len(dt.Parameters())) for _, p := range dt.Parameters() { paramTypeNames = append(paramTypeNames, FormatCELType(p)) } return fmt.Sprintf("%s(%s)", dt.TypeName(), strings.Join(paramTypeNames, ", ")) } func formatExprType(t any) string { if t == nil { return "" } return FormatCheckedType(t.(*exprpb.Type)) } func formatFunctionExprType(resultType *exprpb.Type, argTypes []*exprpb.Type, isInstance bool) string { return formatFunctionInternal[*exprpb.Type](resultType, argTypes, isInstance, formatExprType) } func formatFunctionDeclType(resultType *types.Type, argTypes []*types.Type, isInstance bool) string { return formatFunctionInternal[*types.Type](resultType, argTypes, isInstance, FormatCELType) } func formatFunctionInternal[T any](resultType T, argTypes []T, isInstance bool, format formatter) string { result := "" if isInstance { target := argTypes[0] argTypes = argTypes[1:] result += format(target) result += "." } result += "(" for i, arg := range argTypes { if i > 0 { result += ", " } result += format(arg) } result += ")" rt := format(resultType) if rt != "" { result += " -> " result += rt } return result } // kindOf returns the kind of the type as defined in the checked.proto. func kindOf(t *exprpb.Type) int { if t == nil || t.TypeKind == nil { return kindUnknown } switch t.GetTypeKind().(type) { case *exprpb.Type_Error: return kindError case *exprpb.Type_Function: return kindFunction case *exprpb.Type_Dyn: return kindDyn case *exprpb.Type_Primitive: return kindPrimitive case *exprpb.Type_WellKnown: return kindWellKnown case *exprpb.Type_Wrapper: return kindWrapper case *exprpb.Type_Null: return kindNull case *exprpb.Type_Type: return kindType case *exprpb.Type_ListType_: return kindList case *exprpb.Type_MapType_: return kindMap case *exprpb.Type_MessageType: return kindObject case *exprpb.Type_TypeParam: return kindTypeParam case *exprpb.Type_AbstractType_: return kindAbstract } return kindUnknown }