master
go 149 lines 3.48 KB
Raw
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 }