...

Source file src/github.com/PaesslerAG/gval/evaluable.go

Documentation: github.com/PaesslerAG/gval

     1  package gval
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"regexp"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  // Selector allows for custom variable selection from structs
    13  //
    14  // Return value is again handled with variable() until end of the given path
    15  type Selector interface {
    16  	SelectGVal(c context.Context, key string) (interface{}, error)
    17  }
    18  
    19  // Evaluable evaluates given parameter
    20  type Evaluable func(c context.Context, parameter interface{}) (interface{}, error)
    21  
    22  //EvalInt evaluates given parameter to an int
    23  func (e Evaluable) EvalInt(c context.Context, parameter interface{}) (int, error) {
    24  	v, err := e(c, parameter)
    25  	if err != nil {
    26  		return 0, err
    27  	}
    28  
    29  	f, ok := convertToFloat(v)
    30  	if !ok {
    31  		return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
    32  	}
    33  	return int(f), nil
    34  }
    35  
    36  //EvalFloat64 evaluates given parameter to a float64
    37  func (e Evaluable) EvalFloat64(c context.Context, parameter interface{}) (float64, error) {
    38  	v, err := e(c, parameter)
    39  	if err != nil {
    40  		return 0, err
    41  	}
    42  
    43  	f, ok := convertToFloat(v)
    44  	if !ok {
    45  		return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
    46  	}
    47  	return f, nil
    48  }
    49  
    50  //EvalBool evaluates given parameter to a bool
    51  func (e Evaluable) EvalBool(c context.Context, parameter interface{}) (bool, error) {
    52  	v, err := e(c, parameter)
    53  	if err != nil {
    54  		return false, err
    55  	}
    56  
    57  	b, ok := convertToBool(v)
    58  	if !ok {
    59  		return false, fmt.Errorf("expected bool but got %v (%T)", v, v)
    60  	}
    61  	return b, nil
    62  }
    63  
    64  //EvalString evaluates given parameter to a string
    65  func (e Evaluable) EvalString(c context.Context, parameter interface{}) (string, error) {
    66  	o, err := e(c, parameter)
    67  	if err != nil {
    68  		return "", err
    69  	}
    70  	return fmt.Sprintf("%v", o), nil
    71  }
    72  
    73  //Const Evaluable represents given constant
    74  func (*Parser) Const(value interface{}) Evaluable {
    75  	return constant(value)
    76  }
    77  
    78  //go:noinline
    79  func constant(value interface{}) Evaluable {
    80  	return func(c context.Context, v interface{}) (interface{}, error) {
    81  		return value, nil
    82  	}
    83  }
    84  
    85  //Var Evaluable represents value at given path.
    86  //It supports with default language VariableSelector:
    87  //	map[interface{}]interface{},
    88  //	map[string]interface{} and
    89  // 	[]interface{} and via reflect
    90  //	struct fields,
    91  //	struct methods,
    92  //	slices and
    93  //  map with int or string key.
    94  func (p *Parser) Var(path ...Evaluable) Evaluable {
    95  	if p.selector == nil {
    96  		return variable(path)
    97  	}
    98  	return p.selector(path)
    99  }
   100  
   101  // Evaluables is a slice of Evaluable.
   102  type Evaluables []Evaluable
   103  
   104  // EvalStrings evaluates given parameter to a string slice
   105  func (evs Evaluables) EvalStrings(c context.Context, parameter interface{}) ([]string, error) {
   106  	strs := make([]string, len(evs))
   107  	for i, p := range evs {
   108  		k, err := p.EvalString(c, parameter)
   109  		if err != nil {
   110  			return nil, err
   111  		}
   112  		strs[i] = k
   113  	}
   114  	return strs, nil
   115  }
   116  
   117  func variable(path Evaluables) Evaluable {
   118  	return func(c context.Context, v interface{}) (interface{}, error) {
   119  		keys, err := path.EvalStrings(c, v)
   120  		if err != nil {
   121  			return nil, err
   122  		}
   123  		for i, k := range keys {
   124  			switch o := v.(type) {
   125  			case Selector:
   126  				v, err = o.SelectGVal(c, k)
   127  				if err != nil {
   128  					return nil, fmt.Errorf("failed to select '%s' on %T: %w", k, o, err)
   129  				}
   130  				continue
   131  			case map[interface{}]interface{}:
   132  				v = o[k]
   133  				continue
   134  			case map[string]interface{}:
   135  				v = o[k]
   136  				continue
   137  			case []interface{}:
   138  				if i, err := strconv.Atoi(k); err == nil && i >= 0 && len(o) > i {
   139  					v = o[i]
   140  					continue
   141  				}
   142  			default:
   143  				var ok bool
   144  				v, ok = reflectSelect(k, o)
   145  				if !ok {
   146  					return nil, fmt.Errorf("unknown parameter %s", strings.Join(keys[:i+1], "."))
   147  				}
   148  			}
   149  		}
   150  		return v, nil
   151  	}
   152  }
   153  
   154  func reflectSelect(key string, value interface{}) (selection interface{}, ok bool) {
   155  	vv := reflect.ValueOf(value)
   156  	vvElem := resolvePotentialPointer(vv)
   157  
   158  	switch vvElem.Kind() {
   159  	case reflect.Map:
   160  		mapKey, ok := reflectConvertTo(vv.Type().Key().Kind(), key)
   161  		if !ok {
   162  			return nil, false
   163  		}
   164  
   165  		vvElem = vv.MapIndex(reflect.ValueOf(mapKey))
   166  		vvElem = resolvePotentialPointer(vvElem)
   167  
   168  		if vvElem.IsValid() {
   169  			return vvElem.Interface(), true
   170  		}
   171  	case reflect.Slice:
   172  		if i, err := strconv.Atoi(key); err == nil && i >= 0 && vv.Len() > i {
   173  			vvElem = resolvePotentialPointer(vv.Index(i))
   174  			return vvElem.Interface(), true
   175  		}
   176  	case reflect.Struct:
   177  		field := vvElem.FieldByName(key)
   178  		if field.IsValid() {
   179  			return field.Interface(), true
   180  		}
   181  
   182  		method := vv.MethodByName(key)
   183  		if method.IsValid() {
   184  			return method.Interface(), true
   185  		}
   186  	}
   187  	return nil, false
   188  }
   189  
   190  func resolvePotentialPointer(value reflect.Value) reflect.Value {
   191  	if value.Kind() == reflect.Ptr {
   192  		return value.Elem()
   193  	}
   194  	return value
   195  }
   196  
   197  func reflectConvertTo(k reflect.Kind, value string) (interface{}, bool) {
   198  	switch k {
   199  	case reflect.String:
   200  		return value, true
   201  	case reflect.Int:
   202  		if i, err := strconv.Atoi(value); err == nil {
   203  			return i, true
   204  		}
   205  	}
   206  	return nil, false
   207  }
   208  
   209  func (*Parser) callFunc(fun function, args ...Evaluable) Evaluable {
   210  	return func(c context.Context, v interface{}) (ret interface{}, err error) {
   211  		a := make([]interface{}, len(args))
   212  		for i, arg := range args {
   213  			ai, err := arg(c, v)
   214  			if err != nil {
   215  				return nil, err
   216  			}
   217  			a[i] = ai
   218  		}
   219  		return fun(c, a...)
   220  	}
   221  }
   222  
   223  func (*Parser) callEvaluable(fullname string, fun Evaluable, args ...Evaluable) Evaluable {
   224  	return func(c context.Context, v interface{}) (ret interface{}, err error) {
   225  		f, err := fun(c, v)
   226  
   227  		if err != nil {
   228  			return nil, fmt.Errorf("could not call function: %w", err)
   229  		}
   230  
   231  		defer func() {
   232  			if r := recover(); r != nil {
   233  				err = fmt.Errorf("failed to execute function '%s': %s", fullname, r)
   234  				ret = nil
   235  			}
   236  		}()
   237  
   238  		ff := reflect.ValueOf(f)
   239  
   240  		if ff.Kind() != reflect.Func {
   241  			return nil, fmt.Errorf("could not call '%s' type %T", fullname, f)
   242  		}
   243  
   244  		a := make([]reflect.Value, len(args))
   245  		for i := range args {
   246  			arg, err := args[i](c, v)
   247  			if err != nil {
   248  				return nil, err
   249  			}
   250  			a[i] = reflect.ValueOf(arg)
   251  		}
   252  
   253  		rr := ff.Call(a)
   254  
   255  		r := make([]interface{}, len(rr))
   256  		for i, e := range rr {
   257  			r[i] = e.Interface()
   258  		}
   259  
   260  		errorInterface := reflect.TypeOf((*error)(nil)).Elem()
   261  		if len(r) > 0 && ff.Type().Out(len(r)-1).Implements(errorInterface) {
   262  			if r[len(r)-1] != nil {
   263  				err = r[len(r)-1].(error)
   264  			}
   265  			r = r[0 : len(r)-1]
   266  		}
   267  
   268  		switch len(r) {
   269  		case 0:
   270  			return err, nil
   271  		case 1:
   272  			return r[0], err
   273  		default:
   274  			return r, err
   275  		}
   276  	}
   277  }
   278  
   279  //IsConst returns if the Evaluable is a Parser.Const() value
   280  func (e Evaluable) IsConst() bool {
   281  	pc := reflect.ValueOf(constant(nil)).Pointer()
   282  	pe := reflect.ValueOf(e).Pointer()
   283  	return pc == pe
   284  }
   285  
   286  func regEx(a, b Evaluable) (Evaluable, error) {
   287  	if !b.IsConst() {
   288  		return func(c context.Context, o interface{}) (interface{}, error) {
   289  			a, err := a.EvalString(c, o)
   290  			if err != nil {
   291  				return nil, err
   292  			}
   293  			b, err := b.EvalString(c, o)
   294  			if err != nil {
   295  				return nil, err
   296  			}
   297  			matched, err := regexp.MatchString(b, a)
   298  			return matched, err
   299  		}, nil
   300  	}
   301  	s, err := b.EvalString(context.TODO(), nil)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  	regex, err := regexp.Compile(s)
   306  	if err != nil {
   307  		return nil, err
   308  	}
   309  	return func(c context.Context, v interface{}) (interface{}, error) {
   310  		s, err := a.EvalString(c, v)
   311  		if err != nil {
   312  			return nil, err
   313  		}
   314  		return regex.MatchString(s), nil
   315  	}, nil
   316  }
   317  
   318  func notRegEx(a, b Evaluable) (Evaluable, error) {
   319  	if !b.IsConst() {
   320  		return func(c context.Context, o interface{}) (interface{}, error) {
   321  			a, err := a.EvalString(c, o)
   322  			if err != nil {
   323  				return nil, err
   324  			}
   325  			b, err := b.EvalString(c, o)
   326  			if err != nil {
   327  				return nil, err
   328  			}
   329  			matched, err := regexp.MatchString(b, a)
   330  			return !matched, err
   331  		}, nil
   332  	}
   333  	s, err := b.EvalString(context.TODO(), nil)
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	regex, err := regexp.Compile(s)
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	return func(c context.Context, v interface{}) (interface{}, error) {
   342  		s, err := a.EvalString(c, v)
   343  		if err != nil {
   344  			return nil, err
   345  		}
   346  		return !regex.MatchString(s), nil
   347  	}, nil
   348  }
   349  

View as plain text