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 }