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  }