github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/caveats/types/registration.go (about) 1 package types 2 3 import ( 4 "fmt" 5 6 "github.com/authzed/cel-go/cel" 7 "github.com/authzed/cel-go/common/types/ref" 8 9 "github.com/authzed/spicedb/pkg/genutil" 10 ) 11 12 var definitions = map[string]typeDefinition{} 13 14 // CustomTypes holds the set of custom types defined and exported by this package. This is exported 15 // so that the CEL environment construction can apply the necessary env options for the custom 16 // types. 17 var CustomTypes = map[string][]cel.EnvOption{} 18 19 // CustomMethodsOnTypes holds a set of new methods applied over defined types. This is exported 20 // so that the CEL environment construction can apply the necessary env options to support these methods 21 var CustomMethodsOnTypes []cel.EnvOption 22 23 type ( 24 typedValueConverter func(value any) (any, error) 25 ) 26 27 type typeDefinition struct { 28 // localName is the localized name/keyword for the type. 29 localName string 30 31 // childTypeCount is the number of generics on the type, if any. 32 childTypeCount uint8 33 34 // asVariableType converts the type definition into a VariableType. 35 asVariableType func(childTypes []VariableType) (*VariableType, error) 36 } 37 38 // registerBasicType registers a basic type with the given keyword, CEL type, and converter. 39 func registerBasicType(keyword string, celType *cel.Type, converter typedValueConverter) VariableType { 40 varType := VariableType{ 41 localName: keyword, 42 celType: celType, 43 childTypes: nil, 44 converter: converter, 45 } 46 47 definitions[keyword] = typeDefinition{ 48 localName: keyword, 49 childTypeCount: 0, 50 asVariableType: func(childTypes []VariableType) (*VariableType, error) { 51 return &varType, nil 52 }, 53 } 54 return varType 55 } 56 57 // registerGenericType registers a type with at least one generic. 58 func registerGenericType( 59 keyword string, 60 childTypeCount uint8, 61 asVariableType func(childTypes []VariableType) VariableType, 62 ) func(childTypes ...VariableType) (VariableType, error) { 63 definitions[keyword] = typeDefinition{ 64 localName: keyword, 65 childTypeCount: childTypeCount, 66 asVariableType: func(childTypes []VariableType) (*VariableType, error) { 67 childTypeLength, err := genutil.EnsureUInt8(len(childTypes)) 68 if err != nil { 69 return nil, err 70 } 71 72 if childTypeLength != childTypeCount { 73 return nil, fmt.Errorf("type `%s` requires %d generic types; found %d", keyword, childTypeCount, len(childTypes)) 74 } 75 76 built := asVariableType(childTypes) 77 return &built, nil 78 }, 79 } 80 return func(childTypes ...VariableType) (VariableType, error) { 81 childTypeLength, err := genutil.EnsureUInt8(len(childTypes)) 82 if err != nil { 83 return VariableType{}, err 84 } 85 86 if childTypeLength != childTypeCount { 87 return VariableType{}, fmt.Errorf("invalid number of parameters given to type constructor. expected: %d, found: %d", childTypeCount, len(childTypes)) 88 } 89 90 return asVariableType(childTypes), nil 91 } 92 } 93 94 // registerCustomType registers a custom type that wraps a base CEL type. 95 func registerCustomType[T CustomType](keyword string, baseCelType *cel.Type, converter typedValueConverter, opts ...cel.EnvOption) VariableType { 96 CustomTypes[keyword] = opts 97 return registerBasicType(keyword, baseCelType, converter) 98 } 99 100 func registerMethodOnDefinedType(baseType *cel.Type, name string, args []*cel.Type, returnType *cel.Type, binding func(arg ...ref.Val) ref.Val) { 101 finalArgs := make([]*cel.Type, 0, len(args)+1) 102 finalArgs = append(finalArgs, baseType) 103 finalArgs = append(finalArgs, args...) 104 method := cel.Function(name, cel.MemberOverload(name, finalArgs, returnType, cel.FunctionBinding(binding))) 105 CustomMethodsOnTypes = append(CustomMethodsOnTypes, method) 106 }