github.com/influxdata/telegraf@v1.30.3/internal/templating/node.go (about)

     1  package templating
     2  
     3  import (
     4  	"sort"
     5  	"strings"
     6  )
     7  
     8  // node is an item in a sorted k-ary tree of filter parts. Each child is sorted by its part value.
     9  // The special value of "*", is always sorted last.
    10  type node struct {
    11  	separator string
    12  	value     string
    13  	children  nodes
    14  	template  *Template
    15  }
    16  
    17  // insert inserts the given string template into the tree.  The filter string is separated
    18  // on the template separator and each part is used as the path in the tree.
    19  func (n *node) insert(filter string, template *Template) {
    20  	n.separator = template.separator
    21  	n.recursiveInsert(strings.Split(filter, n.separator), template)
    22  }
    23  
    24  // recursiveInsert does the actual recursive insertion
    25  func (n *node) recursiveInsert(values []string, template *Template) {
    26  	// Add the end, set the template
    27  	if len(values) == 0 {
    28  		n.template = template
    29  		return
    30  	}
    31  
    32  	// See if the current element already exists in the tree. If so, insert the
    33  	// into that sub-tree
    34  	for _, v := range n.children {
    35  		if v.value == values[0] {
    36  			v.recursiveInsert(values[1:], template)
    37  			return
    38  		}
    39  	}
    40  
    41  	// New element, add it to the tree and sort the children
    42  	newNode := &node{value: values[0]}
    43  	n.children = append(n.children, newNode)
    44  	sort.Sort(&n.children)
    45  
    46  	// Now insert the rest of the tree into the new element
    47  	newNode.recursiveInsert(values[1:], template)
    48  }
    49  
    50  // search searches for a template matching the input string
    51  func (n *node) search(line string) *Template {
    52  	separator := n.separator
    53  	return n.recursiveSearch(strings.Split(line, separator))
    54  }
    55  
    56  // recursiveSearch performs the actual recursive search
    57  func (n *node) recursiveSearch(lineParts []string) *Template {
    58  	// nothing to search
    59  	if len(lineParts) == 0 || len(n.children) == 0 {
    60  		return n.template
    61  	}
    62  
    63  	var (
    64  		hasWildcard bool
    65  		length      = len(n.children)
    66  	)
    67  
    68  	// exclude last child from search if it is a wildcard. sort.Search expects
    69  	// a lexicographically sorted set of children and we have artificially sorted
    70  	// wildcards to the end of the child set
    71  	// wildcards will be searched separately if no exact match is found
    72  	if hasWildcard = n.children[length-1].value == "*"; hasWildcard {
    73  		length--
    74  	}
    75  
    76  	i := sort.Search(length, func(i int) bool {
    77  		return n.children[i].value >= lineParts[0]
    78  	})
    79  
    80  	// given an exact match is found within children set
    81  	if i < length && n.children[i].value == lineParts[0] {
    82  		// descend into the matching node
    83  		if tmpl := n.children[i].recursiveSearch(lineParts[1:]); tmpl != nil {
    84  			// given a template is found return it
    85  			return tmpl
    86  		}
    87  	}
    88  
    89  	// given no template is found and the last child is a wildcard
    90  	if hasWildcard {
    91  		// also search the wildcard child node
    92  		return n.children[length].recursiveSearch(lineParts[1:])
    93  	}
    94  
    95  	// fallback to returning template at this node
    96  	return n.template
    97  }
    98  
    99  // nodes is simply an array of nodes implementing the sorting interface.
   100  type nodes []*node
   101  
   102  // Less returns a boolean indicating whether the filter at position j
   103  // is less than the filter at position k. Filters are order by string
   104  // comparison of each component parts.  A wildcard value "*" is never
   105  // less than a non-wildcard value.
   106  //
   107  // For example, the filters:
   108  //
   109  //	"*.*"
   110  //	"servers.*"
   111  //	"servers.localhost"
   112  //	"*.localhost"
   113  //
   114  // Would be sorted as:
   115  //
   116  //	"servers.localhost"
   117  //	"servers.*"
   118  //	"*.localhost"
   119  //	"*.*"
   120  func (n *nodes) Less(j, k int) bool {
   121  	if (*n)[j].value == "*" && (*n)[k].value != "*" {
   122  		return false
   123  	}
   124  
   125  	if (*n)[j].value != "*" && (*n)[k].value == "*" {
   126  		return true
   127  	}
   128  
   129  	return (*n)[j].value < (*n)[k].value
   130  }
   131  
   132  // Swap swaps two elements of the array
   133  func (n *nodes) Swap(i, j int) { (*n)[i], (*n)[j] = (*n)[j], (*n)[i] }
   134  
   135  // Len returns the length of the array
   136  func (n *nodes) Len() int { return len(*n) }