...

Source file src/github.com/PaesslerAG/jsonpath/selector.go

Documentation: github.com/PaesslerAG/jsonpath

     1  package jsonpath
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strconv"
     7  
     8  	"github.com/PaesslerAG/gval"
     9  )
    10  
    11  //plainSelector evaluate exactly one result
    12  type plainSelector func(c context.Context, r, v interface{}) (interface{}, error)
    13  
    14  //ambiguousSelector evaluate wildcard
    15  type ambiguousSelector func(c context.Context, r, v interface{}, match ambiguousMatcher)
    16  
    17  //@
    18  func currentElementSelector() plainSelector {
    19  	return func(c context.Context, r, v interface{}) (interface{}, error) {
    20  		return c.Value(currentElement{}), nil
    21  	}
    22  }
    23  
    24  type currentElement struct{}
    25  
    26  func currentContext(c context.Context, v interface{}) context.Context {
    27  	return context.WithValue(c, currentElement{}, v)
    28  }
    29  
    30  //.x, [x]
    31  func directSelector(key gval.Evaluable) plainSelector {
    32  	return func(c context.Context, r, v interface{}) (interface{}, error) {
    33  
    34  		e, _, err := selectValue(c, key, r, v)
    35  		if err != nil {
    36  			return nil, err
    37  		}
    38  
    39  		return e, nil
    40  	}
    41  }
    42  
    43  // * / [*]
    44  func starSelector() ambiguousSelector {
    45  	return func(c context.Context, r, v interface{}, match ambiguousMatcher) {
    46  		visitAll(v, func(key string, val interface{}) { match(key, val) })
    47  	}
    48  }
    49  
    50  // [x, ...]
    51  func multiSelector(keys []gval.Evaluable) ambiguousSelector {
    52  	if len(keys) == 0 {
    53  		return starSelector()
    54  	}
    55  	return func(c context.Context, r, v interface{}, match ambiguousMatcher) {
    56  		for _, k := range keys {
    57  			e, wildcard, err := selectValue(c, k, r, v)
    58  			if err != nil {
    59  				continue
    60  			}
    61  			match(wildcard, e)
    62  		}
    63  	}
    64  }
    65  
    66  func selectValue(c context.Context, key gval.Evaluable, r, v interface{}) (value interface{}, jkey string, err error) {
    67  	c = currentContext(c, v)
    68  	switch o := v.(type) {
    69  	case []interface{}:
    70  		i, err := key.EvalInt(c, r)
    71  		if err != nil {
    72  			return nil, "", fmt.Errorf("could not select value, invalid key: %s", err)
    73  		}
    74  		if i < 0 || i >= len(o) {
    75  			return nil, "", fmt.Errorf("index %d out of bounds", i)
    76  		}
    77  		return o[i], strconv.Itoa(i), nil
    78  	case map[string]interface{}:
    79  		k, err := key.EvalString(c, r)
    80  		if err != nil {
    81  			return nil, "", fmt.Errorf("could not select value, invalid key: %s", err)
    82  		}
    83  
    84  		if r, ok := o[k]; ok {
    85  			return r, k, nil
    86  		}
    87  		return nil, "", fmt.Errorf("unknown key %s", k)
    88  
    89  	default:
    90  		return nil, "", fmt.Errorf("unsupported value type %T for select, expected map[string]interface{} or []interface{}", o)
    91  	}
    92  }
    93  
    94  //..
    95  func mapperSelector() ambiguousSelector {
    96  	return mapper
    97  }
    98  
    99  func mapper(c context.Context, r, v interface{}, match ambiguousMatcher) {
   100  	match([]interface{}{}, v)
   101  	visitAll(v, func(wildcard string, v interface{}) {
   102  		mapper(c, r, v, func(key interface{}, v interface{}) {
   103  			match(append([]interface{}{wildcard}, key.([]interface{})...), v)
   104  		})
   105  	})
   106  }
   107  
   108  func visitAll(v interface{}, visit func(key string, v interface{})) {
   109  	switch v := v.(type) {
   110  	case []interface{}:
   111  		for i, e := range v {
   112  			k := strconv.Itoa(i)
   113  			visit(k, e)
   114  		}
   115  	case map[string]interface{}:
   116  		for k, e := range v {
   117  			visit(k, e)
   118  		}
   119  	}
   120  
   121  }
   122  
   123  //[? ]
   124  func filterSelector(filter gval.Evaluable) ambiguousSelector {
   125  	return func(c context.Context, r, v interface{}, match ambiguousMatcher) {
   126  		visitAll(v, func(wildcard string, v interface{}) {
   127  			condition, err := filter.EvalBool(currentContext(c, v), r)
   128  			if err != nil {
   129  				return
   130  			}
   131  			if condition {
   132  				match(wildcard, v)
   133  			}
   134  		})
   135  	}
   136  }
   137  
   138  //[::]
   139  func rangeSelector(min, max, step gval.Evaluable) ambiguousSelector {
   140  	return func(c context.Context, r, v interface{}, match ambiguousMatcher) {
   141  		cs, ok := v.([]interface{})
   142  		if !ok {
   143  			return
   144  		}
   145  
   146  		c = currentContext(c, v)
   147  
   148  		min, err := min.EvalInt(c, r)
   149  		if err != nil {
   150  			return
   151  		}
   152  		max, err := max.EvalInt(c, r)
   153  		if err != nil {
   154  			return
   155  		}
   156  		step, err := step.EvalInt(c, r)
   157  		if err != nil {
   158  			return
   159  		}
   160  
   161  		if min > max {
   162  			return
   163  		}
   164  
   165  		n := len(cs)
   166  		min = negmax(min, n)
   167  		max = negmax(max, n)
   168  
   169  		if step == 0 {
   170  			step = 1
   171  		}
   172  
   173  		if step > 0 {
   174  			for i := min; i < max; i += step {
   175  				match(strconv.Itoa(i), cs[i])
   176  			}
   177  		} else {
   178  			for i := max - 1; i >= min; i += step {
   179  				match(strconv.Itoa(i), cs[i])
   180  			}
   181  		}
   182  
   183  	}
   184  }
   185  
   186  func negmax(n, max int) int {
   187  	if n < 0 {
   188  		n = max + n
   189  		if n < 0 {
   190  			n = 0
   191  		}
   192  	} else if n > max {
   193  		return max
   194  	}
   195  	return n
   196  }
   197  
   198  // ()
   199  func newScript(script gval.Evaluable) plainSelector {
   200  	return func(c context.Context, r, v interface{}) (interface{}, error) {
   201  		return script(currentContext(c, v), r)
   202  	}
   203  }
   204  

View as plain text