github.com/expr-lang/expr@v1.16.9/patcher/value/value.go (about)

     1  // Package value provides a Patcher that uses interfaces to allow custom types that can be represented as standard go values to be used more easily in expressions.
     2  package value
     3  
     4  import (
     5  	"reflect"
     6  	"time"
     7  
     8  	"github.com/expr-lang/expr"
     9  	"github.com/expr-lang/expr/ast"
    10  	"github.com/expr-lang/expr/conf"
    11  )
    12  
    13  // ValueGetter is a Patcher that allows custom types to be represented as standard go values for use with expr.
    14  // It also adds the `$patcher_value_getter` function to the program for efficiently calling matching interfaces.
    15  //
    16  // The purpose of this Patcher is to make it seamless to use custom types in expressions without the need to
    17  // first convert them to standard go values. It may also facilitate using already existing structs or maps as
    18  // environments when they contain compatible types.
    19  //
    20  // An example usage may be modeling a database record with columns that have varying data types and constraints.
    21  // In such an example you may have custom types that, beyond storing a simple value, such as an integer, may
    22  // contain metadata such as column type and if a value is specifically a NULL value.
    23  //
    24  // Use it directly as an Option to expr.Compile()
    25  var ValueGetter = expr.Option(func(c *conf.Config) {
    26  	c.Visitors = append(c.Visitors, patcher{})
    27  	getValueFunc(c)
    28  })
    29  
    30  // A AnyValuer provides a generic function for a custom type to return standard go values.
    31  // It allows for returning a `nil` value but does not provide any type checking at expression compile.
    32  //
    33  // A custom type may implement both AnyValuer and a type specific interface to enable both
    34  // compile time checking and the ability to return a `nil` value.
    35  type AnyValuer interface {
    36  	AsAny() any
    37  }
    38  
    39  type IntValuer interface {
    40  	AsInt() int
    41  }
    42  
    43  type BoolValuer interface {
    44  	AsBool() bool
    45  }
    46  
    47  type Int8Valuer interface {
    48  	AsInt8() int8
    49  }
    50  
    51  type Int16Valuer interface {
    52  	AsInt16() int16
    53  }
    54  
    55  type Int32Valuer interface {
    56  	AsInt32() int32
    57  }
    58  
    59  type Int64Valuer interface {
    60  	AsInt64() int64
    61  }
    62  
    63  type UintValuer interface {
    64  	AsUint() uint
    65  }
    66  
    67  type Uint8Valuer interface {
    68  	AsUint8() uint8
    69  }
    70  
    71  type Uint16Valuer interface {
    72  	AsUint16() uint16
    73  }
    74  
    75  type Uint32Valuer interface {
    76  	AsUint32() uint32
    77  }
    78  
    79  type Uint64Valuer interface {
    80  	AsUint64() uint64
    81  }
    82  
    83  type Float32Valuer interface {
    84  	AsFloat32() float32
    85  }
    86  
    87  type Float64Valuer interface {
    88  	AsFloat64() float64
    89  }
    90  
    91  type StringValuer interface {
    92  	AsString() string
    93  }
    94  
    95  type TimeValuer interface {
    96  	AsTime() time.Time
    97  }
    98  
    99  type DurationValuer interface {
   100  	AsDuration() time.Duration
   101  }
   102  
   103  type ArrayValuer interface {
   104  	AsArray() []any
   105  }
   106  
   107  type MapValuer interface {
   108  	AsMap() map[string]any
   109  }
   110  
   111  var supportedInterfaces = []reflect.Type{
   112  	reflect.TypeOf((*AnyValuer)(nil)).Elem(),
   113  	reflect.TypeOf((*BoolValuer)(nil)).Elem(),
   114  	reflect.TypeOf((*IntValuer)(nil)).Elem(),
   115  	reflect.TypeOf((*Int8Valuer)(nil)).Elem(),
   116  	reflect.TypeOf((*Int16Valuer)(nil)).Elem(),
   117  	reflect.TypeOf((*Int32Valuer)(nil)).Elem(),
   118  	reflect.TypeOf((*Int64Valuer)(nil)).Elem(),
   119  	reflect.TypeOf((*UintValuer)(nil)).Elem(),
   120  	reflect.TypeOf((*Uint8Valuer)(nil)).Elem(),
   121  	reflect.TypeOf((*Uint16Valuer)(nil)).Elem(),
   122  	reflect.TypeOf((*Uint32Valuer)(nil)).Elem(),
   123  	reflect.TypeOf((*Uint64Valuer)(nil)).Elem(),
   124  	reflect.TypeOf((*Float32Valuer)(nil)).Elem(),
   125  	reflect.TypeOf((*Float64Valuer)(nil)).Elem(),
   126  	reflect.TypeOf((*StringValuer)(nil)).Elem(),
   127  	reflect.TypeOf((*TimeValuer)(nil)).Elem(),
   128  	reflect.TypeOf((*DurationValuer)(nil)).Elem(),
   129  	reflect.TypeOf((*ArrayValuer)(nil)).Elem(),
   130  	reflect.TypeOf((*MapValuer)(nil)).Elem(),
   131  }
   132  
   133  type patcher struct{}
   134  
   135  func (patcher) Visit(node *ast.Node) {
   136  	switch id := (*node).(type) {
   137  	case *ast.IdentifierNode, *ast.MemberNode:
   138  		nodeType := id.Type()
   139  		for _, t := range supportedInterfaces {
   140  			if nodeType.Implements(t) {
   141  				ast.Patch(node, &ast.CallNode{
   142  					Callee:    &ast.IdentifierNode{Value: "$patcher_value_getter"},
   143  					Arguments: []ast.Node{id},
   144  				})
   145  				return
   146  			}
   147  		}
   148  	}
   149  }
   150  
   151  func getValue(params ...any) (any, error) {
   152  	switch v := params[0].(type) {
   153  	case AnyValuer:
   154  		return v.AsAny(), nil
   155  	case BoolValuer:
   156  		return v.AsBool(), nil
   157  	case IntValuer:
   158  		return v.AsInt(), nil
   159  	case Int8Valuer:
   160  		return v.AsInt8(), nil
   161  	case Int16Valuer:
   162  		return v.AsInt16(), nil
   163  	case Int32Valuer:
   164  		return v.AsInt32(), nil
   165  	case Int64Valuer:
   166  		return v.AsInt64(), nil
   167  	case UintValuer:
   168  		return v.AsUint(), nil
   169  	case Uint8Valuer:
   170  		return v.AsUint8(), nil
   171  	case Uint16Valuer:
   172  		return v.AsUint16(), nil
   173  	case Uint32Valuer:
   174  		return v.AsUint32(), nil
   175  	case Uint64Valuer:
   176  		return v.AsUint64(), nil
   177  	case Float32Valuer:
   178  		return v.AsFloat32(), nil
   179  	case Float64Valuer:
   180  		return v.AsFloat64(), nil
   181  	case StringValuer:
   182  		return v.AsString(), nil
   183  	case TimeValuer:
   184  		return v.AsTime(), nil
   185  	case DurationValuer:
   186  		return v.AsDuration(), nil
   187  	case ArrayValuer:
   188  		return v.AsArray(), nil
   189  	case MapValuer:
   190  		return v.AsMap(), nil
   191  	}
   192  
   193  	return params[0], nil
   194  }
   195  
   196  var getValueFunc = expr.Function("$patcher_value_getter", getValue,
   197  	new(func(BoolValuer) bool),
   198  	new(func(IntValuer) int),
   199  	new(func(Int8Valuer) int8),
   200  	new(func(Int16Valuer) int16),
   201  	new(func(Int32Valuer) int32),
   202  	new(func(Int64Valuer) int64),
   203  	new(func(UintValuer) uint),
   204  	new(func(Uint8Valuer) uint8),
   205  	new(func(Uint16Valuer) uint16),
   206  	new(func(Uint32Valuer) uint32),
   207  	new(func(Uint64Valuer) uint64),
   208  	new(func(Float32Valuer) float32),
   209  	new(func(Float64Valuer) float64),
   210  	new(func(StringValuer) string),
   211  	new(func(TimeValuer) time.Time),
   212  	new(func(DurationValuer) time.Duration),
   213  	new(func(ArrayValuer) []any),
   214  	new(func(MapValuer) map[string]any),
   215  	new(func(any) any),
   216  )