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 )