github.com/netdata/go.d.plugin@v0.58.1/pkg/matcher/matcher.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package matcher
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"regexp"
     9  )
    10  
    11  type (
    12  	// Matcher is an interface that wraps MatchString method.
    13  	Matcher interface {
    14  		// Match performs match against given []byte
    15  		Match(b []byte) bool
    16  		// MatchString performs match against given string
    17  		MatchString(string) bool
    18  	}
    19  
    20  	// Format matcher format
    21  	Format string
    22  )
    23  
    24  const (
    25  	// FmtString is a string match format.
    26  	FmtString Format = "string"
    27  	// FmtGlob is a glob match format.
    28  	FmtGlob Format = "glob"
    29  	// FmtRegExp is a regex match format.
    30  	FmtRegExp Format = "regexp"
    31  	// FmtSimplePattern is a simple pattern match format
    32  	// https://docs.netdata.cloud/libnetdata/simple_pattern/
    33  	FmtSimplePattern Format = "simple_patterns"
    34  
    35  	// Separator is a separator between match format and expression.
    36  	Separator = ":"
    37  )
    38  
    39  const (
    40  	symString = "="
    41  	symGlob   = "*"
    42  	symRegExp = "~"
    43  )
    44  
    45  var (
    46  	reShortSyntax = regexp.MustCompile(`(?s)^(!)?(.)\s*(.*)$`)
    47  	reLongSyntax  = regexp.MustCompile(`(?s)^(!)?([^:]+):(.*)$`)
    48  
    49  	errNotShortSyntax = errors.New("not short syntax")
    50  )
    51  
    52  // Must is a helper that wraps a call to a function returning (Matcher, error) and panics if the error is non-nil.
    53  // It is intended for use in variable initializations such as
    54  //
    55  //	var m = matcher.Must(matcher.New(matcher.FmtString, "hello world"))
    56  func Must(m Matcher, err error) Matcher {
    57  	if err != nil {
    58  		panic(err)
    59  	}
    60  	return m
    61  }
    62  
    63  // New create a matcher
    64  func New(format Format, expr string) (Matcher, error) {
    65  	switch format {
    66  	case FmtString:
    67  		return NewStringMatcher(expr, true, true)
    68  	case FmtGlob:
    69  		return NewGlobMatcher(expr)
    70  	case FmtRegExp:
    71  		return NewRegExpMatcher(expr)
    72  	case FmtSimplePattern:
    73  		return NewSimplePatternsMatcher(expr)
    74  	default:
    75  		return nil, fmt.Errorf("unsupported matcher format: '%s'", format)
    76  	}
    77  }
    78  
    79  // Parse parses line and returns appropriate matcher based on matched format.
    80  //
    81  // Short Syntax
    82  //
    83  //	<line>      ::= [ <not> ] <format> <space> <expr>
    84  //	<not>       ::= '!'
    85  //	                  negative expression
    86  //	<format>    ::= [ '=', '~', '*' ]
    87  //	                  '=' means string match
    88  //	                  '~' means regexp match
    89  //	                  '*' means glob match
    90  //	<space>     ::= { ' ' | '\t' | '\n' | '\n' | '\r' }
    91  //	<expr>      ::= any string
    92  //
    93  // Long Syntax
    94  //
    95  //	<line>      ::= [ <not> ] <format> <separator> <expr>
    96  //	<format>    ::= [ 'string' | 'glob' | 'regexp' | 'simple_patterns' ]
    97  //	<not>       ::= '!'
    98  //	                  negative expression
    99  //	<separator> ::= ':'
   100  //	<expr>      ::= any string
   101  func Parse(line string) (Matcher, error) {
   102  	matcher, err := parseShortFormat(line)
   103  	if err == nil {
   104  		return matcher, nil
   105  	}
   106  	return parseLongSyntax(line)
   107  }
   108  
   109  func parseShortFormat(line string) (Matcher, error) {
   110  	m := reShortSyntax.FindStringSubmatch(line)
   111  	if m == nil {
   112  		return nil, errNotShortSyntax
   113  	}
   114  	var format Format
   115  	switch m[2] {
   116  	case symString:
   117  		format = FmtString
   118  	case symGlob:
   119  		format = FmtGlob
   120  	case symRegExp:
   121  		format = FmtRegExp
   122  	default:
   123  		return nil, fmt.Errorf("invalid short syntax: unknown symbol '%s'", m[2])
   124  	}
   125  	expr := m[3]
   126  	matcher, err := New(format, expr)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	if m[1] != "" {
   131  		matcher = Not(matcher)
   132  	}
   133  	return matcher, nil
   134  }
   135  
   136  func parseLongSyntax(line string) (Matcher, error) {
   137  	m := reLongSyntax.FindStringSubmatch(line)
   138  	if m == nil {
   139  		return nil, fmt.Errorf("invalid syntax")
   140  	}
   141  	matcher, err := New(Format(m[2]), m[3])
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	if m[1] != "" {
   146  		matcher = Not(matcher)
   147  	}
   148  	return matcher, nil
   149  }