...

Source file src/github.com/pelletier/go-toml/query/match.go

Documentation: github.com/pelletier/go-toml/query

     1  package query
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	"github.com/pelletier/go-toml"
     8  )
     9  
    10  // base match
    11  type matchBase struct {
    12  	next pathFn
    13  }
    14  
    15  func (f *matchBase) setNext(next pathFn) {
    16  	f.next = next
    17  }
    18  
    19  // terminating functor - gathers results
    20  type terminatingFn struct {
    21  	// empty
    22  }
    23  
    24  func newTerminatingFn() *terminatingFn {
    25  	return &terminatingFn{}
    26  }
    27  
    28  func (f *terminatingFn) setNext(next pathFn) {
    29  	// do nothing
    30  }
    31  
    32  func (f *terminatingFn) call(node interface{}, ctx *queryContext) {
    33  	ctx.result.appendResult(node, ctx.lastPosition)
    34  }
    35  
    36  // match single key
    37  type matchKeyFn struct {
    38  	matchBase
    39  	Name string
    40  }
    41  
    42  func newMatchKeyFn(name string) *matchKeyFn {
    43  	return &matchKeyFn{Name: name}
    44  }
    45  
    46  func (f *matchKeyFn) call(node interface{}, ctx *queryContext) {
    47  	if array, ok := node.([]*toml.Tree); ok {
    48  		for _, tree := range array {
    49  			item := tree.GetPath([]string{f.Name})
    50  			if item != nil {
    51  				ctx.lastPosition = tree.GetPositionPath([]string{f.Name})
    52  				f.next.call(item, ctx)
    53  			}
    54  		}
    55  	} else if tree, ok := node.(*toml.Tree); ok {
    56  		item := tree.GetPath([]string{f.Name})
    57  		if item != nil {
    58  			ctx.lastPosition = tree.GetPositionPath([]string{f.Name})
    59  			f.next.call(item, ctx)
    60  		}
    61  	}
    62  }
    63  
    64  // match single index
    65  type matchIndexFn struct {
    66  	matchBase
    67  	Idx int
    68  }
    69  
    70  func newMatchIndexFn(idx int) *matchIndexFn {
    71  	return &matchIndexFn{Idx: idx}
    72  }
    73  
    74  func (f *matchIndexFn) call(node interface{}, ctx *queryContext) {
    75  	v := reflect.ValueOf(node)
    76  	if v.Kind() == reflect.Slice {
    77  		if v.Len() == 0 {
    78  			return
    79  		}
    80  
    81  		// Manage negative values
    82  		idx := f.Idx
    83  		if idx < 0 {
    84  			idx += v.Len()
    85  		}
    86  		if 0 <= idx && idx < v.Len() {
    87  			callNextIndexSlice(f.next, node, ctx, v.Index(idx).Interface())
    88  		}
    89  	}
    90  }
    91  
    92  func callNextIndexSlice(next pathFn, node interface{}, ctx *queryContext, value interface{}) {
    93  	if treesArray, ok := node.([]*toml.Tree); ok {
    94  		ctx.lastPosition = treesArray[0].Position()
    95  	}
    96  	next.call(value, ctx)
    97  }
    98  
    99  // filter by slicing
   100  type matchSliceFn struct {
   101  	matchBase
   102  	Start, End, Step *int
   103  }
   104  
   105  func newMatchSliceFn() *matchSliceFn {
   106  	return &matchSliceFn{}
   107  }
   108  
   109  func (f *matchSliceFn) setStart(start int) *matchSliceFn {
   110  	f.Start = &start
   111  	return f
   112  }
   113  
   114  func (f *matchSliceFn) setEnd(end int) *matchSliceFn {
   115  	f.End = &end
   116  	return f
   117  }
   118  
   119  func (f *matchSliceFn) setStep(step int) *matchSliceFn {
   120  	f.Step = &step
   121  	return f
   122  }
   123  
   124  func (f *matchSliceFn) call(node interface{}, ctx *queryContext) {
   125  	v := reflect.ValueOf(node)
   126  	if v.Kind() == reflect.Slice {
   127  		if v.Len() == 0 {
   128  			return
   129  		}
   130  
   131  		var start, end, step int
   132  
   133  		// Initialize step
   134  		if f.Step != nil {
   135  			step = *f.Step
   136  		} else {
   137  			step = 1
   138  		}
   139  
   140  		// Initialize start
   141  		if f.Start != nil {
   142  			start = *f.Start
   143  			// Manage negative values
   144  			if start < 0 {
   145  				start += v.Len()
   146  			}
   147  			// Manage out of range values
   148  			start = max(start, 0)
   149  			start = min(start, v.Len()-1)
   150  		} else if step > 0 {
   151  			start = 0
   152  		} else {
   153  			start = v.Len() - 1
   154  		}
   155  
   156  		// Initialize end
   157  		if f.End != nil {
   158  			end = *f.End
   159  			// Manage negative values
   160  			if end < 0 {
   161  				end += v.Len()
   162  			}
   163  			// Manage out of range values
   164  			end = max(end, -1)
   165  			end = min(end, v.Len())
   166  		} else if step > 0 {
   167  			end = v.Len()
   168  		} else {
   169  			end = -1
   170  		}
   171  
   172  		// Loop on values
   173  		if step > 0 {
   174  			for idx := start; idx < end; idx += step {
   175  				callNextIndexSlice(f.next, node, ctx, v.Index(idx).Interface())
   176  			}
   177  		} else {
   178  			for idx := start; idx > end; idx += step {
   179  				callNextIndexSlice(f.next, node, ctx, v.Index(idx).Interface())
   180  			}
   181  		}
   182  	}
   183  }
   184  
   185  func min(a, b int) int {
   186  	if a < b {
   187  		return a
   188  	}
   189  	return b
   190  }
   191  
   192  func max(a, b int) int {
   193  	if a > b {
   194  		return a
   195  	}
   196  	return b
   197  }
   198  
   199  // match anything
   200  type matchAnyFn struct {
   201  	matchBase
   202  }
   203  
   204  func newMatchAnyFn() *matchAnyFn {
   205  	return &matchAnyFn{}
   206  }
   207  
   208  func (f *matchAnyFn) call(node interface{}, ctx *queryContext) {
   209  	if tree, ok := node.(*toml.Tree); ok {
   210  		for _, k := range tree.Keys() {
   211  			v := tree.GetPath([]string{k})
   212  			ctx.lastPosition = tree.GetPositionPath([]string{k})
   213  			f.next.call(v, ctx)
   214  		}
   215  	}
   216  }
   217  
   218  // filter through union
   219  type matchUnionFn struct {
   220  	Union []pathFn
   221  }
   222  
   223  func (f *matchUnionFn) setNext(next pathFn) {
   224  	for _, fn := range f.Union {
   225  		fn.setNext(next)
   226  	}
   227  }
   228  
   229  func (f *matchUnionFn) call(node interface{}, ctx *queryContext) {
   230  	for _, fn := range f.Union {
   231  		fn.call(node, ctx)
   232  	}
   233  }
   234  
   235  // match every single last node in the tree
   236  type matchRecursiveFn struct {
   237  	matchBase
   238  }
   239  
   240  func newMatchRecursiveFn() *matchRecursiveFn {
   241  	return &matchRecursiveFn{}
   242  }
   243  
   244  func (f *matchRecursiveFn) call(node interface{}, ctx *queryContext) {
   245  	originalPosition := ctx.lastPosition
   246  	if tree, ok := node.(*toml.Tree); ok {
   247  		var visit func(tree *toml.Tree)
   248  		visit = func(tree *toml.Tree) {
   249  			for _, k := range tree.Keys() {
   250  				v := tree.GetPath([]string{k})
   251  				ctx.lastPosition = tree.GetPositionPath([]string{k})
   252  				f.next.call(v, ctx)
   253  				switch node := v.(type) {
   254  				case *toml.Tree:
   255  					visit(node)
   256  				case []*toml.Tree:
   257  					for _, subtree := range node {
   258  						visit(subtree)
   259  					}
   260  				}
   261  			}
   262  		}
   263  		ctx.lastPosition = originalPosition
   264  		f.next.call(tree, ctx)
   265  		visit(tree)
   266  	}
   267  }
   268  
   269  // match based on an externally provided functional filter
   270  type matchFilterFn struct {
   271  	matchBase
   272  	Pos  toml.Position
   273  	Name string
   274  }
   275  
   276  func newMatchFilterFn(name string, pos toml.Position) *matchFilterFn {
   277  	return &matchFilterFn{Name: name, Pos: pos}
   278  }
   279  
   280  func (f *matchFilterFn) call(node interface{}, ctx *queryContext) {
   281  	fn, ok := (*ctx.filters)[f.Name]
   282  	if !ok {
   283  		panic(fmt.Sprintf("%s: query context does not have filter '%s'",
   284  			f.Pos.String(), f.Name))
   285  	}
   286  	switch castNode := node.(type) {
   287  	case *toml.Tree:
   288  		for _, k := range castNode.Keys() {
   289  			v := castNode.GetPath([]string{k})
   290  			if fn(v) {
   291  				ctx.lastPosition = castNode.GetPositionPath([]string{k})
   292  				f.next.call(v, ctx)
   293  			}
   294  		}
   295  	case []*toml.Tree:
   296  		for _, v := range castNode {
   297  			if fn(v) {
   298  				if len(castNode) > 0 {
   299  					ctx.lastPosition = castNode[0].Position()
   300  				}
   301  				f.next.call(v, ctx)
   302  			}
   303  		}
   304  	case []interface{}:
   305  		for _, v := range castNode {
   306  			if fn(v) {
   307  				f.next.call(v, ctx)
   308  			}
   309  		}
   310  	}
   311  }
   312  

View as plain text